1use std::collections::HashMap;
7use std::fs::File;
8use std::io::{Read as IoRead, Write as IoWrite};
9use std::path::{Path, PathBuf};
10
11use chrono::{DateTime, Utc};
12use flate2::read::GzDecoder;
13use fs2::FileExt;
14use serde::{Deserialize, Serialize};
15use sha2::{Digest, Sha256};
16
17pub mod affect;
18pub mod bridges;
19pub mod cache;
20pub mod metrics;
21pub mod channel;
22pub mod crypto;
23pub mod encryption;
24pub mod format;
25pub mod query;
26pub mod semantic;
27pub mod temporal;
28pub mod types;
29pub mod workspace;
30pub mod contracts;
31
32pub use affect::*;
33pub use bridges::*;
34pub use channel::*;
35pub use crypto::*;
36pub use encryption::*;
37pub use format::*;
38pub use query::*;
39pub use semantic::*;
40pub use temporal::*;
41pub use types::*;
42pub use workspace::*;
43
44#[derive(thiserror::Error, Debug)]
50pub enum CommError {
51 #[error("Invalid channel name: {0}")]
53 InvalidChannelName(String),
54
55 #[error("Invalid message content: {0}")]
57 InvalidContent(String),
58
59 #[error("Invalid sender: {0}")]
61 InvalidSender(String),
62
63 #[error("Channel not found: {0}")]
65 ChannelNotFound(u64),
66
67 #[error("Message not found: {0}")]
69 MessageNotFound(u64),
70
71 #[error("Subscription not found: {0}")]
73 SubscriptionNotFound(u64),
74
75 #[error("Channel {0} has reached maximum participants")]
77 ChannelFull(u64),
78
79 #[error("Participant '{0}' is not in channel {1}")]
81 NotInChannel(String, u64),
82
83 #[error("Participant '{0}' is already in channel {1}")]
85 AlreadyInChannel(String, u64),
86
87 #[error("Channel {0} is {1} — operation not allowed")]
89 ChannelStateViolation(u64, String),
90
91 #[error("Dead letter index {0} out of bounds")]
93 DeadLetterNotFound(usize),
94
95 #[error("IO error: {0}")]
97 Io(#[from] std::io::Error),
98
99 #[error("Serialization error: {0}")]
101 Serialization(String),
102
103 #[error("Invalid .acomm file: {0}")]
105 InvalidFile(String),
106
107 #[error("Consent error: {0}")]
109 ConsentError(String),
110
111 #[error("Trust error: {0}")]
113 TrustError(String),
114
115 #[error("Temporal error: {0}")]
117 TemporalError(String),
118
119 #[error("Federation error: {0}")]
121 FederationError(String),
122
123 #[error("Hive error: {0}")]
125 HiveError(String),
126
127 #[error("Consent denied: {reason}")]
129 ConsentDenied { reason: String },
130
131 #[error("Rate limit exceeded: {limit}")]
133 RateLimitExceeded { limit: String },
134
135 #[error("Lock error: {0}")]
137 LockError(String),
138
139 #[error("Key not found: {0}")]
141 KeyNotFound(u64),
142
143 #[error("Not found: {0}")]
145 NotFound(String),
146}
147
148pub type CommResult<T> = Result<T, CommError>;
150
151pub const MAX_CONTENT_SIZE: usize = 1_048_576;
157
158pub const ACOMM_MAGIC: &[u8; 8] = b"ACOMM001";
160
161pub const ACOMM_VERSION: u32 = 1;
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166pub enum MessageType {
167 Text,
169 Command,
171 Query,
173 Response,
175 Broadcast,
177 Notification,
179 Acknowledgment,
181 Error,
183}
184
185impl std::fmt::Display for MessageType {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 match self {
188 MessageType::Text => write!(f, "text"),
189 MessageType::Command => write!(f, "command"),
190 MessageType::Query => write!(f, "query"),
191 MessageType::Response => write!(f, "response"),
192 MessageType::Broadcast => write!(f, "broadcast"),
193 MessageType::Notification => write!(f, "notification"),
194 MessageType::Acknowledgment => write!(f, "acknowledgment"),
195 MessageType::Error => write!(f, "error"),
196 }
197 }
198}
199
200impl std::str::FromStr for MessageType {
201 type Err = String;
202 fn from_str(s: &str) -> Result<Self, Self::Err> {
203 match s.to_lowercase().as_str() {
204 "text" => Ok(MessageType::Text),
205 "command" => Ok(MessageType::Command),
206 "query" => Ok(MessageType::Query),
207 "response" => Ok(MessageType::Response),
208 "broadcast" => Ok(MessageType::Broadcast),
209 "notification" => Ok(MessageType::Notification),
210 "acknowledgment" | "ack" => Ok(MessageType::Acknowledgment),
211 "error" => Ok(MessageType::Error),
212 other => Err(format!("Unknown message type: {other}")),
213 }
214 }
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
223pub enum MessageStatus {
224 Created,
226 Sent,
228 Delivered,
230 Read,
232 Acknowledged,
234 Failed,
236 Expired,
238 DeadLettered,
240}
241
242impl Default for MessageStatus {
243 fn default() -> Self {
244 MessageStatus::Created
245 }
246}
247
248impl std::fmt::Display for MessageStatus {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 match self {
251 MessageStatus::Created => write!(f, "created"),
252 MessageStatus::Sent => write!(f, "sent"),
253 MessageStatus::Delivered => write!(f, "delivered"),
254 MessageStatus::Read => write!(f, "read"),
255 MessageStatus::Acknowledged => write!(f, "acknowledged"),
256 MessageStatus::Failed => write!(f, "failed"),
257 MessageStatus::Expired => write!(f, "expired"),
258 MessageStatus::DeadLettered => write!(f, "dead_lettered"),
259 }
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
269pub enum MessagePriority {
270 Low = 0,
272 Normal = 1,
274 High = 2,
276 Urgent = 3,
278 Critical = 4,
280}
281
282impl Default for MessagePriority {
283 fn default() -> Self {
284 MessagePriority::Normal
285 }
286}
287
288impl std::fmt::Display for MessagePriority {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 match self {
291 MessagePriority::Low => write!(f, "low"),
292 MessagePriority::Normal => write!(f, "normal"),
293 MessagePriority::High => write!(f, "high"),
294 MessagePriority::Urgent => write!(f, "urgent"),
295 MessagePriority::Critical => write!(f, "critical"),
296 }
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
306pub enum ChannelState {
307 Active,
309 Paused,
311 Draining,
313 Closed,
315 Archived,
317 SilentCommunion,
319 HiveMode,
321 PendingConsent,
323}
324
325impl Default for ChannelState {
326 fn default() -> Self {
327 ChannelState::Active
328 }
329}
330
331impl std::fmt::Display for ChannelState {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 match self {
334 ChannelState::Active => write!(f, "active"),
335 ChannelState::Paused => write!(f, "paused"),
336 ChannelState::Draining => write!(f, "draining"),
337 ChannelState::Closed => write!(f, "closed"),
338 ChannelState::Archived => write!(f, "archived"),
339 ChannelState::SilentCommunion => write!(f, "silent_communion"),
340 ChannelState::HiveMode => write!(f, "hive_mode"),
341 ChannelState::PendingConsent => write!(f, "pending_consent"),
342 }
343 }
344}
345
346#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
352pub enum DeliveryMode {
353 AtMostOnce,
355 AtLeastOnce,
357 ExactlyOnce,
359}
360
361impl Default for DeliveryMode {
362 fn default() -> Self {
363 DeliveryMode::AtLeastOnce
364 }
365}
366
367#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
373pub enum RetentionPolicy {
374 Forever,
376 Duration(u64),
378 MessageCount(u64),
380}
381
382impl Default for RetentionPolicy {
383 fn default() -> Self {
384 RetentionPolicy::Forever
385 }
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
394pub enum DeadLetterReason {
395 ChannelClosed,
397 ChannelNotFound,
399 RecipientUnavailable,
401 MaxRetriesExceeded,
403 Expired,
405 ValidationFailed(String),
407}
408
409impl std::fmt::Display for DeadLetterReason {
410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411 match self {
412 DeadLetterReason::ChannelClosed => write!(f, "channel_closed"),
413 DeadLetterReason::ChannelNotFound => write!(f, "channel_not_found"),
414 DeadLetterReason::RecipientUnavailable => write!(f, "recipient_unavailable"),
415 DeadLetterReason::MaxRetriesExceeded => write!(f, "max_retries_exceeded"),
416 DeadLetterReason::Expired => write!(f, "expired"),
417 DeadLetterReason::ValidationFailed(s) => write!(f, "validation_failed: {s}"),
418 }
419 }
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct DeadLetter {
425 pub original_message: Message,
427 pub reason: DeadLetterReason,
429 pub dead_lettered_at: DateTime<Utc>,
431 pub retry_count: u32,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct KeyEntry {
442 pub id: u64,
444 pub algorithm: String,
446 pub created_at: u64,
448 pub status: String,
450 pub channel_id: Option<u64>,
452 pub fingerprint: String,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct Message {
463 pub id: u64,
465 pub channel_id: u64,
467 pub sender: String,
469 pub recipient: Option<String>,
471 pub content: String,
473 pub message_type: MessageType,
475 pub timestamp: DateTime<Utc>,
477 pub metadata: HashMap<String, String>,
479 pub signature: Option<String>,
481 #[serde(default)]
483 pub acknowledged_by: Vec<String>,
484 #[serde(default)]
486 pub status: MessageStatus,
487 #[serde(default)]
489 pub priority: MessagePriority,
490 #[serde(default)]
492 pub reply_to: Option<u64>,
493 #[serde(default)]
495 pub correlation_id: Option<String>,
496 #[serde(default)]
498 pub thread_id: Option<String>,
499 #[serde(default)]
501 pub comm_timestamp: CommTimestamp,
502 #[serde(default)]
505 pub rich_content_json: Option<String>,
506 #[serde(default)]
508 pub comm_id: Option<CommId>,
509 #[serde(default)]
511 pub receipt_id: Option<String>,
512}
513
514#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
516pub enum ChannelType {
517 Direct,
519 Group,
521 Broadcast,
523 PubSub,
525 Telepathic,
527 Hive,
529 Temporal,
531 Destiny,
533 Oracle,
535}
536
537impl std::fmt::Display for ChannelType {
538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539 match self {
540 ChannelType::Direct => write!(f, "direct"),
541 ChannelType::Group => write!(f, "group"),
542 ChannelType::Broadcast => write!(f, "broadcast"),
543 ChannelType::PubSub => write!(f, "pubsub"),
544 ChannelType::Telepathic => write!(f, "telepathic"),
545 ChannelType::Hive => write!(f, "hive"),
546 ChannelType::Temporal => write!(f, "temporal"),
547 ChannelType::Destiny => write!(f, "destiny"),
548 ChannelType::Oracle => write!(f, "oracle"),
549 }
550 }
551}
552
553impl std::str::FromStr for ChannelType {
554 type Err = String;
555 fn from_str(s: &str) -> Result<Self, Self::Err> {
556 match s.to_lowercase().as_str() {
557 "direct" => Ok(ChannelType::Direct),
558 "group" => Ok(ChannelType::Group),
559 "broadcast" => Ok(ChannelType::Broadcast),
560 "pubsub" => Ok(ChannelType::PubSub),
561 "telepathic" => Ok(ChannelType::Telepathic),
562 "hive" => Ok(ChannelType::Hive),
563 "temporal" => Ok(ChannelType::Temporal),
564 "destiny" => Ok(ChannelType::Destiny),
565 "oracle" => Ok(ChannelType::Oracle),
566 other => Err(format!("Unknown channel type: {other}")),
567 }
568 }
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct ChannelConfig {
574 pub max_participants: u32,
576 pub ttl_seconds: u64,
578 pub persistence: bool,
580 pub encryption_required: bool,
582 #[serde(default)]
584 pub delivery_mode: DeliveryMode,
585 #[serde(default)]
587 pub retention_policy: RetentionPolicy,
588 #[serde(default)]
591 pub min_trust_level: Option<CommTrustLevel>,
592}
593
594impl Default for ChannelConfig {
595 fn default() -> Self {
596 Self {
597 max_participants: 0,
598 ttl_seconds: 0,
599 persistence: true,
600 encryption_required: false,
601 delivery_mode: DeliveryMode::default(),
602 retention_policy: RetentionPolicy::default(),
603 min_trust_level: None,
604 }
605 }
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct Channel {
611 pub id: u64,
613 pub name: String,
615 pub channel_type: ChannelType,
617 pub created_at: DateTime<Utc>,
619 pub participants: Vec<String>,
621 pub config: ChannelConfig,
623 #[serde(default)]
625 pub state: ChannelState,
626 #[serde(default)]
628 pub comm_id: Option<CommId>,
629 #[serde(default)]
631 pub contract_ref: Option<String>,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct Subscription {
637 pub id: u64,
639 pub topic: String,
641 pub subscriber: String,
643 pub created_at: DateTime<Utc>,
645}
646
647#[derive(Debug, Clone, Default, Serialize, Deserialize)]
649pub struct MessageFilter {
650 pub since: Option<DateTime<Utc>>,
652 pub before: Option<DateTime<Utc>>,
654 pub sender: Option<String>,
656 pub message_type: Option<MessageType>,
658 pub limit: Option<usize>,
660 #[serde(default)]
662 pub priority: Option<u32>,
663 #[serde(default)]
665 pub thread_id: Option<u64>,
666 #[serde(default)]
668 pub content_contains: Option<String>,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct AcommHeader {
674 pub magic: [u8; 8],
676 pub version: u32,
678 pub channel_count: u32,
680 pub message_count: u64,
682}
683
684#[derive(Debug, Clone, Default)]
690pub struct RateTracker {
691 pub message_count_minute: u32,
693 pub last_minute_reset: u64,
695 pub message_count_hour: u32,
697 pub last_hour_reset: u64,
699}
700
701pub struct CommFileLock {
711 lock_file: File,
712 lock_path: PathBuf,
713}
714
715impl CommFileLock {
716 pub fn acquire(data_path: &Path) -> CommResult<Self> {
718 let lock_path = data_path.with_extension("acomm.lock");
719 let lock_file = File::create(&lock_path).map_err(|e| {
720 CommError::LockError(format!("Failed to create lock file: {}", e))
721 })?;
722 lock_file.lock_exclusive().map_err(|e| {
723 CommError::LockError(format!("Failed to acquire exclusive lock: {}", e))
724 })?;
725 Ok(Self { lock_file, lock_path })
726 }
727
728 pub fn try_acquire(data_path: &Path) -> CommResult<Self> {
733 let lock_path = data_path.with_extension("acomm.lock");
734 let lock_file = File::create(&lock_path).map_err(|e| {
735 CommError::LockError(format!("Failed to create lock file: {}", e))
736 })?;
737 lock_file.try_lock_exclusive().map_err(|e| {
738 CommError::LockError(format!("Failed to acquire lock (already held): {}", e))
739 })?;
740 Ok(Self { lock_file, lock_path })
741 }
742
743 pub fn acquire_shared(data_path: &Path) -> CommResult<Self> {
749 let lock_path = data_path.with_extension("acomm.lock");
750 let lock_file = File::create(&lock_path).map_err(|e| {
751 CommError::LockError(format!("Failed to create lock file: {}", e))
752 })?;
753 lock_file.lock_shared().map_err(|e| {
754 CommError::LockError(format!("Failed to acquire shared lock: {}", e))
755 })?;
756 Ok(Self { lock_file, lock_path })
757 }
758
759 pub fn release(self) -> CommResult<()> {
761 self.lock_file.unlock().map_err(|e| {
762 CommError::LockError(format!("Failed to release lock: {}", e))
763 })?;
764 let _ = std::fs::remove_file(&self.lock_path);
766 Ok(())
767 }
768
769 pub fn recover_stale(data_path: &Path, max_age_secs: u64) -> CommResult<bool> {
774 let lock_path = data_path.with_extension("acomm.lock");
775 if lock_path.exists() {
776 if let Ok(metadata) = std::fs::metadata(&lock_path) {
777 if let Ok(modified) = metadata.modified() {
778 if let Ok(elapsed) = modified.elapsed() {
779 if elapsed.as_secs() > max_age_secs {
780 let _ = std::fs::remove_file(&lock_path);
781 return Ok(true);
782 }
783 }
784 }
785 }
786 }
787 Ok(false)
788 }
789}
790
791impl Drop for CommFileLock {
792 fn drop(&mut self) {
793 let _ = self.lock_file.unlock();
794 let _ = std::fs::remove_file(&self.lock_path);
795 }
796}
797
798#[derive(Debug, Clone, Serialize, Deserialize)]
804pub struct CommStore {
805 pub channels: HashMap<u64, Channel>,
807 pub messages: HashMap<u64, Message>,
809 pub subscriptions: HashMap<u64, Subscription>,
811 next_channel_id: u64,
813 next_message_id: u64,
815 next_subscription_id: u64,
817 #[serde(default)]
819 pub dead_letters: Vec<DeadLetter>,
820
821 #[serde(default)]
823 pub consent_gates: Vec<ConsentGateEntry>,
824
825 #[serde(default)]
827 pub trust_levels: HashMap<String, CommTrustLevel>,
828
829 #[serde(default)]
831 pub temporal_queue: Vec<TemporalMessage>,
832
833 #[serde(default = "default_one")]
835 next_temporal_id: u64,
836
837 #[serde(default)]
839 pub federation_config: FederationConfig,
840
841 #[serde(default)]
843 pub hive_minds: HashMap<u64, HiveMind>,
844
845 #[serde(default = "default_one")]
847 next_hive_id: u64,
848
849 #[serde(default)]
851 pub comm_log: Vec<CommunicationLogEntry>,
852
853 #[serde(default = "default_one")]
855 next_log_index: u64,
856
857 #[serde(default)]
859 pub audit_log: Vec<AuditEntry>,
860
861 #[serde(default)]
863 pub rate_limit_config: RateLimitConfig,
864
865 #[serde(default)]
867 pub semantic_operations: Vec<SemanticOperation>,
868
869 #[serde(default = "default_one")]
871 next_semantic_id: u64,
872
873 #[serde(default)]
875 pub semantic_conflicts: Vec<SemanticConflict>,
876
877 #[serde(default)]
879 pub affect_states: HashMap<String, AffectState>,
880
881 #[serde(default = "default_resistance")]
883 pub affect_resistance: f64,
884
885 #[serde(default)]
887 pub pending_consent_requests: Vec<ConsentRequest>,
888
889 #[serde(default)]
891 pub meld_sessions: Vec<MeldSession>,
892
893 #[serde(default)]
895 pub zone_policies: HashMap<String, ZonePolicyConfig>,
896
897 #[serde(default)]
899 pub key_store: Vec<KeyEntry>,
900
901 #[serde(default = "default_one")]
903 next_key_id: u64,
904
905 #[serde(default)]
907 pub lamport_counter: u64,
908
909 #[serde(skip)]
911 pub rate_trackers: HashMap<String, RateTracker>,
912
913 #[serde(skip)]
916 pub key_pair: Option<CommKeyPair>,
917
918 #[serde(default)]
920 pub agents: HashMap<String, CommunicatingAgent>,
921
922 #[serde(skip)]
925 pub bridge_config: BridgeConfig,
926
927 #[serde(default)]
932 pub embeddings: HashMap<u64, Vec<f32>>,
933}
934
935fn default_one() -> u64 {
936 1
937}
938
939fn default_resistance() -> f64 {
940 0.5
941}
942
943impl Default for CommStore {
944 fn default() -> Self {
945 Self::new()
946 }
947}
948
949impl CommStore {
950 pub fn new() -> Self {
952 Self {
953 channels: HashMap::new(),
954 messages: HashMap::new(),
955 subscriptions: HashMap::new(),
956 next_channel_id: 1,
957 next_message_id: 1,
958 next_subscription_id: 1,
959 dead_letters: Vec::new(),
960 consent_gates: Vec::new(),
961 trust_levels: HashMap::new(),
962 temporal_queue: Vec::new(),
963 next_temporal_id: 1,
964 federation_config: FederationConfig::default(),
965 hive_minds: HashMap::new(),
966 next_hive_id: 1,
967 comm_log: Vec::new(),
968 next_log_index: 1,
969 audit_log: Vec::new(),
970 rate_limit_config: RateLimitConfig::default(),
971 semantic_operations: Vec::new(),
972 next_semantic_id: 1,
973 semantic_conflicts: Vec::new(),
974 affect_states: HashMap::new(),
975 affect_resistance: 0.5,
976 pending_consent_requests: Vec::new(),
977 meld_sessions: Vec::new(),
978 zone_policies: HashMap::new(),
979 key_store: Vec::new(),
980 next_key_id: 1,
981 lamport_counter: 0,
982 rate_trackers: HashMap::new(),
983 key_pair: None,
984 agents: HashMap::new(),
985 bridge_config: BridgeConfig::default(),
986 embeddings: HashMap::new(),
987 }
988 }
989
990 pub fn set_bridge_config(&mut self, config: BridgeConfig) {
996 self.bridge_config = config;
997 }
998
999 pub fn register_agent(&mut self, agent: CommunicatingAgent) -> CommResult<()> {
1005 let agent_id = agent.agent_id.clone();
1006 self.agents.insert(agent_id.clone(), agent);
1007 self.log_audit(
1008 AuditEventType::AgentRegistered,
1009 &agent_id,
1010 &format!("Agent registered: {}", agent_id),
1011 Some(agent_id.clone()),
1012 );
1013 Ok(())
1014 }
1015
1016 pub fn get_agent(&self, agent_id: &str) -> Option<&CommunicatingAgent> {
1018 self.agents.get(agent_id)
1019 }
1020
1021 pub fn list_agents(&self) -> Vec<&CommunicatingAgent> {
1023 self.agents.values().collect()
1024 }
1025
1026 pub fn update_agent_availability(
1028 &mut self,
1029 agent_id: &str,
1030 availability: Availability,
1031 ) -> CommResult<()> {
1032 if let Some(agent) = self.agents.get_mut(agent_id) {
1033 agent.availability = availability;
1034 Ok(())
1035 } else {
1036 Err(CommError::NotFound(format!("Agent not found: {}", agent_id)))
1037 }
1038 }
1039
1040 pub fn unregister_agent(&mut self, agent_id: &str) -> CommResult<()> {
1042 if self.agents.remove(agent_id).is_some() {
1043 self.log_audit(
1044 AuditEventType::AgentUnregistered,
1045 agent_id,
1046 &format!("Agent unregistered: {}", agent_id),
1047 Some(agent_id.to_string()),
1048 );
1049 Ok(())
1050 } else {
1051 Err(CommError::NotFound(format!("Agent not found: {}", agent_id)))
1052 }
1053 }
1054
1055 fn validate_channel_name(name: &str) -> CommResult<()> {
1060 if name.is_empty() || name.len() > 128 {
1061 return Err(CommError::InvalidChannelName(
1062 "Channel name must be 1-128 characters".to_string(),
1063 ));
1064 }
1065 if !name
1066 .chars()
1067 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
1068 {
1069 return Err(CommError::InvalidChannelName(
1070 "Channel name must contain only alphanumeric characters, hyphens, or underscores"
1071 .to_string(),
1072 ));
1073 }
1074 Ok(())
1075 }
1076
1077 fn validate_content(content: &str) -> CommResult<()> {
1078 if content.is_empty() {
1079 return Err(CommError::InvalidContent(
1080 "Message content cannot be empty".to_string(),
1081 ));
1082 }
1083 if content.len() > MAX_CONTENT_SIZE {
1084 return Err(CommError::InvalidContent(format!(
1085 "Message content exceeds maximum size of {} bytes",
1086 MAX_CONTENT_SIZE
1087 )));
1088 }
1089 Ok(())
1090 }
1091
1092 fn validate_sender(sender: &str) -> CommResult<()> {
1093 if sender.is_empty() {
1094 return Err(CommError::InvalidSender(
1095 "Sender cannot be empty".to_string(),
1096 ));
1097 }
1098 Ok(())
1099 }
1100
1101 fn compute_sha256_signature(content: &str) -> String {
1103 let mut hasher = Sha256::new();
1104 hasher.update(content.as_bytes());
1105 format!("{:x}", hasher.finalize())
1106 }
1107
1108 fn compute_signature(&self, content: &str) -> String {
1114 match &self.key_pair {
1115 Some(kp) => kp.sign(content),
1116 None => Self::compute_sha256_signature(content),
1117 }
1118 }
1119
1120 pub fn set_signing_key(&mut self, key_pair: CommKeyPair) {
1122 self.key_pair = Some(key_pair);
1123 }
1124
1125 pub fn get_public_key(&self) -> Option<String> {
1127 self.key_pair.as_ref().map(|kp| kp.public_key_hex())
1128 }
1129
1130 pub fn verify_message_signature(&mut self, message_id: u64) -> bool {
1139 let (content, stored_sig, sender) = match self.messages.get(&message_id) {
1140 Some(msg) => (
1141 msg.content.clone(),
1142 msg.signature.clone(),
1143 msg.sender.clone(),
1144 ),
1145 None => return true,
1146 };
1147 match stored_sig {
1148 Some(ref sig) => {
1149 let valid = if sig.len() == 128 {
1152 if let Some(ref kp) = self.key_pair {
1154 crypto::verify_signature(&kp.public_key_hex(), &content, sig)
1155 } else {
1156 false
1158 }
1159 } else {
1160 let expected = Self::compute_sha256_signature(&content);
1162 *sig == expected
1163 };
1164
1165 if !valid {
1166 self.log_audit(
1167 AuditEventType::SignatureWarning,
1168 &sender,
1169 &format!(
1170 "Signature mismatch for message {}: stored={}",
1171 message_id, sig
1172 ),
1173 Some(message_id.to_string()),
1174 );
1175 }
1176 valid
1177 }
1178 None => true,
1179 }
1180 }
1181
1182 fn check_send_consent(
1186 &self,
1187 channel_id: u64,
1188 sender: &str,
1189 content: &str,
1190 ) -> CommResult<()> {
1191 let channel = match self.channels.get(&channel_id) {
1192 Some(ch) => ch,
1193 None => return Ok(()),
1194 };
1195
1196 let is_rich_content = content.starts_with("[affect:");
1198
1199 if !is_rich_content {
1200 return Ok(());
1201 }
1202
1203 for participant in &channel.participants {
1206 if participant == sender {
1207 continue;
1208 }
1209 let has_consent = self.consent_gates.iter().any(|e| {
1210 e.grantor == *participant
1211 && e.grantee == sender
1212 && e.scope == ConsentScope::SendMessages
1213 && e.status == ConsentStatus::Granted
1214 });
1215 if !has_consent {
1216 return Err(CommError::ConsentDenied {
1217 reason: format!(
1218 "Participant '{}' has not granted SendMessages consent to '{}'",
1219 participant, sender
1220 ),
1221 });
1222 }
1223 }
1224
1225 Ok(())
1226 }
1227
1228 fn check_rate_limit(&mut self, sender: &str) -> CommResult<()> {
1231 let now_epoch = Utc::now().timestamp() as u64;
1232 let limit_per_minute = self.rate_limit_config.messages_per_minute;
1233
1234 let tracker = self
1235 .rate_trackers
1236 .entry(sender.to_string())
1237 .or_default();
1238
1239 if now_epoch - tracker.last_minute_reset >= 60 {
1241 tracker.message_count_minute = 0;
1242 tracker.last_minute_reset = now_epoch;
1243 }
1244
1245 if now_epoch - tracker.last_hour_reset >= 3600 {
1247 tracker.message_count_hour = 0;
1248 tracker.last_hour_reset = now_epoch;
1249 }
1250
1251 if tracker.message_count_minute >= limit_per_minute {
1253 return Err(CommError::RateLimitExceeded {
1254 limit: format!(
1255 "{} messages per minute (sender: {})",
1256 limit_per_minute, sender
1257 ),
1258 });
1259 }
1260
1261 tracker.message_count_minute += 1;
1263 tracker.message_count_hour += 1;
1264
1265 Ok(())
1266 }
1267
1268 fn check_channel_allows_send(&self, channel_id: u64) -> CommResult<()> {
1271 let channel = self
1272 .channels
1273 .get(&channel_id)
1274 .ok_or(CommError::ChannelNotFound(channel_id))?;
1275
1276 match channel.state {
1277 ChannelState::Active => Ok(()),
1278 ChannelState::SilentCommunion | ChannelState::HiveMode => Ok(()),
1279 ChannelState::Paused => Err(CommError::ChannelStateViolation(
1280 channel_id,
1281 "paused".to_string(),
1282 )),
1283 ChannelState::Draining => Err(CommError::ChannelStateViolation(
1284 channel_id,
1285 "draining".to_string(),
1286 )),
1287 ChannelState::Closed => Err(CommError::ChannelStateViolation(
1288 channel_id,
1289 "closed".to_string(),
1290 )),
1291 ChannelState::Archived => Err(CommError::ChannelStateViolation(
1292 channel_id,
1293 "archived".to_string(),
1294 )),
1295 ChannelState::PendingConsent => Err(CommError::ChannelStateViolation(
1296 channel_id,
1297 "pending_consent".to_string(),
1298 )),
1299 }
1300 }
1301
1302 fn check_channel_allows_receive(&self, channel_id: u64) -> CommResult<()> {
1304 let channel = self
1305 .channels
1306 .get(&channel_id)
1307 .ok_or(CommError::ChannelNotFound(channel_id))?;
1308
1309 match channel.state {
1310 ChannelState::Active | ChannelState::Draining => Ok(()),
1311 ChannelState::SilentCommunion | ChannelState::HiveMode => Ok(()),
1312 ChannelState::Archived => Ok(()),
1313 ChannelState::Paused => Err(CommError::ChannelStateViolation(
1314 channel_id,
1315 "paused".to_string(),
1316 )),
1317 ChannelState::Closed => Err(CommError::ChannelStateViolation(
1318 channel_id,
1319 "closed".to_string(),
1320 )),
1321 ChannelState::PendingConsent => Err(CommError::ChannelStateViolation(
1322 channel_id,
1323 "pending_consent".to_string(),
1324 )),
1325 }
1326 }
1327
1328 fn check_trust_for_channel(&self, agent: &str, channel_id: u64) -> CommResult<()> {
1334 let channel = self
1335 .channels
1336 .get(&channel_id)
1337 .ok_or(CommError::ChannelNotFound(channel_id))?;
1338 if let Some(min_trust) = channel.config.min_trust_level {
1339 let agent_trust = self.get_trust_level(agent);
1340 if agent_trust < min_trust {
1341 return Err(CommError::TrustError(format!(
1342 "Trust level insufficient: {:?} < {:?}",
1343 agent_trust, min_trust
1344 )));
1345 }
1346 }
1347 Ok(())
1348 }
1349
1350 fn check_consent_for_action(&self, agent: &str, _resource: &str, scope: ConsentScope) -> bool {
1360 let scope_gates: Vec<&ConsentGateEntry> = self
1362 .consent_gates
1363 .iter()
1364 .filter(|e| e.scope == scope)
1365 .collect();
1366
1367 if scope_gates.is_empty() {
1369 return true;
1370 }
1371
1372 scope_gates.iter().any(|e| {
1374 e.grantee == agent && e.status == ConsentStatus::Granted
1375 })
1376 }
1377
1378 pub fn pause_channel(&mut self, channel_id: u64) -> CommResult<()> {
1384 let channel = self
1385 .channels
1386 .get_mut(&channel_id)
1387 .ok_or(CommError::ChannelNotFound(channel_id))?;
1388 channel.state = ChannelState::Paused;
1389 Ok(())
1390 }
1391
1392 pub fn resume_channel(&mut self, channel_id: u64) -> CommResult<()> {
1394 let channel = self
1395 .channels
1396 .get_mut(&channel_id)
1397 .ok_or(CommError::ChannelNotFound(channel_id))?;
1398 channel.state = ChannelState::Active;
1399 Ok(())
1400 }
1401
1402 pub fn drain_channel(&mut self, channel_id: u64) -> CommResult<()> {
1404 let channel = self
1405 .channels
1406 .get_mut(&channel_id)
1407 .ok_or(CommError::ChannelNotFound(channel_id))?;
1408 channel.state = ChannelState::Draining;
1409 Ok(())
1410 }
1411
1412 pub fn close_channel(&mut self, channel_id: u64) -> CommResult<()> {
1414 let channel = self
1415 .channels
1416 .get_mut(&channel_id)
1417 .ok_or(CommError::ChannelNotFound(channel_id))?;
1418 let name = channel.name.clone();
1419 channel.state = ChannelState::Closed;
1420
1421 self.log_audit(
1423 AuditEventType::ChannelClosed,
1424 "system",
1425 &format!("Closed channel '{}' (id={})", name, channel_id),
1426 Some(channel_id.to_string()),
1427 );
1428
1429 Ok(())
1430 }
1431
1432 pub fn send_message(
1442 &mut self,
1443 channel_id: u64,
1444 sender: &str,
1445 content: &str,
1446 msg_type: MessageType,
1447 ) -> CommResult<Message> {
1448 Self::validate_sender(sender)?;
1449 Self::validate_content(content)?;
1450
1451 self.check_rate_limit(sender)?;
1453
1454 self.check_send_consent(channel_id, sender, content)?;
1456
1457 if !self.channels.contains_key(&channel_id) {
1459 let id = self.next_message_id;
1461 self.next_message_id += 1;
1462 let msg = Message {
1463 id,
1464 channel_id,
1465 sender: sender.to_string(),
1466 recipient: None,
1467 content: content.to_string(),
1468 message_type: msg_type,
1469 timestamp: Utc::now(),
1470 metadata: HashMap::new(),
1471 signature: Some(self.compute_signature(content)),
1472 acknowledged_by: Vec::new(),
1473 status: MessageStatus::DeadLettered,
1474 priority: MessagePriority::default(),
1475 reply_to: None,
1476 correlation_id: None,
1477 thread_id: None,
1478 comm_timestamp: CommTimestamp::default(),
1479 rich_content_json: None,
1480 comm_id: None,
1481 receipt_id: None,
1482 };
1483 self.dead_letters.push(DeadLetter {
1484 original_message: msg,
1485 reason: DeadLetterReason::ChannelNotFound,
1486 dead_lettered_at: Utc::now(),
1487 retry_count: 0,
1488 });
1489 return Err(CommError::ChannelNotFound(channel_id));
1490 }
1491
1492 self.check_trust_for_channel(sender, channel_id)?;
1494
1495 if let Err(e) = self.check_channel_allows_send(channel_id) {
1497 let id = self.next_message_id;
1498 self.next_message_id += 1;
1499 let channel_state = self.channels.get(&channel_id).unwrap().state;
1500 let reason = match channel_state {
1501 ChannelState::Closed => DeadLetterReason::ChannelClosed,
1502 _ => DeadLetterReason::ValidationFailed(format!(
1503 "Channel is {}",
1504 channel_state
1505 )),
1506 };
1507 let msg = Message {
1508 id,
1509 channel_id,
1510 sender: sender.to_string(),
1511 recipient: None,
1512 content: content.to_string(),
1513 message_type: msg_type,
1514 timestamp: Utc::now(),
1515 metadata: HashMap::new(),
1516 signature: Some(self.compute_signature(content)),
1517 acknowledged_by: Vec::new(),
1518 status: MessageStatus::DeadLettered,
1519 priority: MessagePriority::default(),
1520 reply_to: None,
1521 correlation_id: None,
1522 thread_id: None,
1523 comm_timestamp: CommTimestamp::default(),
1524 rich_content_json: None,
1525 comm_id: None,
1526 receipt_id: None,
1527 };
1528 self.dead_letters.push(DeadLetter {
1529 original_message: msg,
1530 reason,
1531 dead_lettered_at: Utc::now(),
1532 retry_count: 0,
1533 });
1534 return Err(e);
1535 }
1536
1537 let id = self.next_message_id;
1538 self.next_message_id += 1;
1539
1540 self.lamport_counter += 1;
1542 let mut ts = CommTimestamp::now(sender);
1543 ts.lamport = self.lamport_counter;
1544 ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
1545
1546 let message = Message {
1547 id,
1548 channel_id,
1549 sender: sender.to_string(),
1550 recipient: None,
1551 content: content.to_string(),
1552 message_type: msg_type,
1553 timestamp: Utc::now(),
1554 metadata: HashMap::new(),
1555 signature: Some(self.compute_signature(content)),
1556 acknowledged_by: Vec::new(),
1557 status: MessageStatus::Sent,
1558 priority: MessagePriority::default(),
1559 reply_to: None,
1560 correlation_id: None,
1561 thread_id: None,
1562 comm_timestamp: ts,
1563 rich_content_json: None,
1564 comm_id: None,
1565 receipt_id: None,
1566 };
1567
1568 self.messages.insert(id, message.clone());
1569
1570 self.log_audit(
1572 AuditEventType::MessageSent,
1573 sender,
1574 &format!("Sent {} message to channel {}", msg_type, channel_id),
1575 Some(id.to_string()),
1576 );
1577
1578 Ok(message)
1581 }
1582
1583 pub fn send_message_with_priority(
1585 &mut self,
1586 channel_id: u64,
1587 sender: &str,
1588 content: &str,
1589 msg_type: MessageType,
1590 priority: MessagePriority,
1591 ) -> CommResult<Message> {
1592 let mut msg = self.send_message(channel_id, sender, content, msg_type)?;
1593 if let Some(stored) = self.messages.get_mut(&msg.id) {
1595 stored.priority = priority;
1596 msg.priority = priority;
1597 }
1598 Ok(msg)
1599 }
1600
1601 pub fn receive_messages(
1607 &mut self,
1608 channel_id: u64,
1609 recipient: Option<&str>,
1610 since: Option<DateTime<Utc>>,
1611 ) -> CommResult<Vec<Message>> {
1612 if !self.channels.contains_key(&channel_id) {
1613 return Err(CommError::ChannelNotFound(channel_id));
1614 }
1615
1616 self.check_channel_allows_receive(channel_id)?;
1618
1619 let mut msgs: Vec<Message> = self
1620 .messages
1621 .values()
1622 .filter(|m| {
1623 if m.channel_id != channel_id {
1624 return false;
1625 }
1626 if let Some(ref recip) = recipient {
1627 if let Some(ref msg_recip) = m.recipient {
1628 if msg_recip != recip {
1629 return false;
1630 }
1631 }
1632 }
1633 if let Some(ref s) = since {
1634 if m.timestamp < *s {
1635 return false;
1636 }
1637 }
1638 true
1639 })
1640 .cloned()
1641 .collect();
1642
1643 msgs.sort_by_key(|m| m.timestamp);
1644
1645 let msg_ids: Vec<u64> = msgs.iter().map(|m| m.id).collect();
1647 for msg_id in msg_ids {
1648 self.verify_message_signature(msg_id);
1649 }
1650
1651 for m in &msgs {
1655 if m.comm_timestamp.lamport > self.lamport_counter {
1656 self.lamport_counter = m.comm_timestamp.lamport;
1657 }
1658 }
1659
1660 Ok(msgs)
1661 }
1662
1663 pub fn acknowledge_message(&mut self, message_id: u64, recipient: &str) -> CommResult<()> {
1665 Self::validate_sender(recipient)?;
1666
1667 let message = self
1668 .messages
1669 .get_mut(&message_id)
1670 .ok_or(CommError::MessageNotFound(message_id))?;
1671
1672 if !message.acknowledged_by.contains(&recipient.to_string()) {
1673 message.acknowledged_by.push(recipient.to_string());
1674 }
1675 message.status = MessageStatus::Acknowledged;
1676 Ok(())
1677 }
1678
1679 pub fn broadcast(
1681 &mut self,
1682 channel_id: u64,
1683 sender: &str,
1684 content: &str,
1685 ) -> CommResult<Vec<Message>> {
1686 Self::validate_sender(sender)?;
1687 Self::validate_content(content)?;
1688
1689 self.check_trust_for_channel(sender, channel_id)?;
1691
1692 let channel = self
1693 .channels
1694 .get(&channel_id)
1695 .ok_or(CommError::ChannelNotFound(channel_id))?
1696 .clone();
1697
1698 let mut delivered = Vec::new();
1699
1700 for participant in &channel.participants {
1701 if participant == sender {
1702 continue;
1703 }
1704
1705 let id = self.next_message_id;
1706 self.next_message_id += 1;
1707
1708 self.lamport_counter += 1;
1710 let mut ts = CommTimestamp::now(sender);
1711 ts.lamport = self.lamport_counter;
1712 ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
1713
1714 let message = Message {
1715 id,
1716 channel_id,
1717 sender: sender.to_string(),
1718 recipient: Some(participant.clone()),
1719 content: content.to_string(),
1720 message_type: MessageType::Broadcast,
1721 timestamp: Utc::now(),
1722 metadata: HashMap::new(),
1723 signature: Some(self.compute_signature(content)),
1724 acknowledged_by: Vec::new(),
1725 status: MessageStatus::Sent,
1726 priority: MessagePriority::default(),
1727 reply_to: None,
1728 correlation_id: None,
1729 thread_id: None,
1730 comm_timestamp: ts,
1731 rich_content_json: None,
1732 comm_id: None,
1733 receipt_id: None,
1734 };
1735
1736 self.messages.insert(id, message.clone());
1737 delivered.push(message);
1738 }
1739
1740 Ok(delivered)
1741 }
1742
1743 pub fn send_reply(
1749 &mut self,
1750 channel_id: u64,
1751 message_id: u64,
1752 sender: &str,
1753 content: &str,
1754 msg_type: MessageType,
1755 ) -> CommResult<Message> {
1756 Self::validate_sender(sender)?;
1757 Self::validate_content(content)?;
1758
1759 let parent = self
1761 .messages
1762 .get(&message_id)
1763 .ok_or(CommError::MessageNotFound(message_id))?
1764 .clone();
1765
1766 self.check_channel_allows_send(channel_id)?;
1768
1769 let thread_id = parent
1771 .thread_id
1772 .clone()
1773 .unwrap_or_else(|| format!("thread-{}", parent.id));
1774
1775 let id = self.next_message_id;
1776 self.next_message_id += 1;
1777
1778 self.lamport_counter += 1;
1780 let mut ts = CommTimestamp::now(sender);
1781 ts.lamport = self.lamport_counter;
1782 ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
1783
1784 let message = Message {
1785 id,
1786 channel_id,
1787 sender: sender.to_string(),
1788 recipient: None,
1789 content: content.to_string(),
1790 message_type: msg_type,
1791 timestamp: Utc::now(),
1792 metadata: HashMap::new(),
1793 signature: Some(self.compute_signature(content)),
1794 acknowledged_by: Vec::new(),
1795 status: MessageStatus::Sent,
1796 priority: MessagePriority::default(),
1797 reply_to: Some(message_id),
1798 correlation_id: None,
1799 thread_id: Some(thread_id),
1800 comm_timestamp: ts,
1801 rich_content_json: None,
1802 comm_id: None,
1803 receipt_id: None,
1804 };
1805
1806 self.messages.insert(id, message.clone());
1807
1808 if let Some(parent_msg) = self.messages.get_mut(&message_id) {
1810 if parent_msg.thread_id.is_none() {
1811 parent_msg.thread_id = Some(format!("thread-{}", parent_msg.id));
1812 }
1813 }
1814
1815 Ok(message)
1816 }
1817
1818 pub fn get_thread(&self, thread_id: &str) -> Vec<Message> {
1820 let mut msgs: Vec<Message> = self
1821 .messages
1822 .values()
1823 .filter(|m| m.thread_id.as_deref() == Some(thread_id))
1824 .cloned()
1825 .collect();
1826 msgs.sort_by_key(|m| m.timestamp);
1827 msgs
1828 }
1829
1830 pub fn get_replies(&self, message_id: u64) -> Vec<Message> {
1832 let mut replies: Vec<Message> = self
1833 .messages
1834 .values()
1835 .filter(|m| m.reply_to == Some(message_id))
1836 .cloned()
1837 .collect();
1838 replies.sort_by_key(|m| m.timestamp);
1839 replies
1840 }
1841
1842 pub fn create_channel(
1848 &mut self,
1849 name: &str,
1850 channel_type: ChannelType,
1851 config: Option<ChannelConfig>,
1852 ) -> CommResult<Channel> {
1853 Self::validate_channel_name(name)?;
1854
1855 let id = self.next_channel_id;
1856 self.next_channel_id += 1;
1857
1858 let channel = Channel {
1859 id,
1860 name: name.to_string(),
1861 channel_type,
1862 created_at: Utc::now(),
1863 participants: Vec::new(),
1864 config: config.unwrap_or_default(),
1865 state: ChannelState::Active,
1866 comm_id: None,
1867 contract_ref: None,
1868 };
1869
1870 self.channels.insert(id, channel.clone());
1871
1872 self.log_audit(
1874 AuditEventType::ChannelCreated,
1875 "system",
1876 &format!("Created channel '{}' (type={}, id={})", name, channel_type, id),
1877 Some(id.to_string()),
1878 );
1879
1880 Ok(channel)
1881 }
1882
1883 pub fn join_channel(&mut self, channel_id: u64, participant: &str) -> CommResult<()> {
1885 Self::validate_sender(participant)?;
1886
1887 self.check_trust_for_channel(participant, channel_id)?;
1891
1892 if !self.check_consent_for_action(participant, &self.channels.get(&channel_id)
1894 .map(|c| c.name.clone()).unwrap_or_default(), ConsentScope::JoinChannels)
1895 {
1896 return Err(CommError::ConsentDenied {
1897 reason: "Consent not granted for joining channels".to_string(),
1898 });
1899 }
1900
1901 let channel = self
1902 .channels
1903 .get_mut(&channel_id)
1904 .ok_or(CommError::ChannelNotFound(channel_id))?;
1905
1906 if channel.participants.contains(&participant.to_string()) {
1907 return Err(CommError::AlreadyInChannel(
1908 participant.to_string(),
1909 channel_id,
1910 ));
1911 }
1912
1913 if channel.config.max_participants > 0
1914 && channel.participants.len() >= channel.config.max_participants as usize
1915 {
1916 return Err(CommError::ChannelFull(channel_id));
1917 }
1918
1919 channel.participants.push(participant.to_string());
1920 Ok(())
1921 }
1922
1923 pub fn leave_channel(&mut self, channel_id: u64, participant: &str) -> CommResult<()> {
1925 let channel = self
1926 .channels
1927 .get_mut(&channel_id)
1928 .ok_or(CommError::ChannelNotFound(channel_id))?;
1929
1930 let pos = channel
1931 .participants
1932 .iter()
1933 .position(|p| p == participant)
1934 .ok_or_else(|| CommError::NotInChannel(participant.to_string(), channel_id))?;
1935
1936 channel.participants.remove(pos);
1937 Ok(())
1938 }
1939
1940 pub fn list_channels(&self) -> Vec<Channel> {
1942 let mut channels: Vec<Channel> = self.channels.values().cloned().collect();
1943 channels.sort_by_key(|c| c.id);
1944 channels
1945 }
1946
1947 pub fn get_channel(&self, channel_id: u64) -> Option<Channel> {
1949 self.channels.get(&channel_id).cloned()
1950 }
1951
1952 pub fn set_channel_config(
1954 &mut self,
1955 channel_id: u64,
1956 config: ChannelConfig,
1957 ) -> CommResult<()> {
1958 let channel = self
1959 .channels
1960 .get_mut(&channel_id)
1961 .ok_or(CommError::ChannelNotFound(channel_id))?;
1962 channel.config = config;
1963 Ok(())
1964 }
1965
1966 pub fn subscribe(&mut self, topic: &str, subscriber: &str) -> CommResult<Subscription> {
1972 Self::validate_sender(subscriber)?;
1973 Self::validate_channel_name(topic)?;
1974
1975 let id = self.next_subscription_id;
1976 self.next_subscription_id += 1;
1977
1978 let subscription = Subscription {
1979 id,
1980 topic: topic.to_string(),
1981 subscriber: subscriber.to_string(),
1982 created_at: Utc::now(),
1983 };
1984
1985 self.subscriptions.insert(id, subscription.clone());
1986 Ok(subscription)
1987 }
1988
1989 pub fn unsubscribe(&mut self, subscription_id: u64) -> CommResult<()> {
1991 if self.subscriptions.remove(&subscription_id).is_none() {
1992 return Err(CommError::SubscriptionNotFound(subscription_id));
1993 }
1994 Ok(())
1995 }
1996
1997 pub fn publish(
1999 &mut self,
2000 topic: &str,
2001 sender: &str,
2002 content: &str,
2003 ) -> CommResult<Vec<Message>> {
2004 Self::validate_sender(sender)?;
2005 Self::validate_content(content)?;
2006
2007 let channel_id = self
2009 .channels
2010 .values()
2011 .find(|c| c.name == topic && c.channel_type == ChannelType::PubSub)
2012 .map(|c| c.id);
2013
2014 let channel_id = match channel_id {
2015 Some(id) => id,
2016 None => {
2017 let ch = self.create_channel(topic, ChannelType::PubSub, None)?;
2018 ch.id
2019 }
2020 };
2021
2022 self.check_trust_for_channel(sender, channel_id)?;
2024
2025 let subscribers: Vec<String> = self
2027 .subscriptions
2028 .values()
2029 .filter(|s| s.topic == topic && s.subscriber != sender)
2030 .map(|s| s.subscriber.clone())
2031 .collect();
2032
2033 let mut delivered = Vec::new();
2034
2035 for subscriber in subscribers {
2036 let id = self.next_message_id;
2037 self.next_message_id += 1;
2038
2039 self.lamport_counter += 1;
2041 let mut ts = CommTimestamp::now(sender);
2042 ts.lamport = self.lamport_counter;
2043 ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
2044
2045 let message = Message {
2046 id,
2047 channel_id,
2048 sender: sender.to_string(),
2049 recipient: Some(subscriber),
2050 content: content.to_string(),
2051 message_type: MessageType::Notification,
2052 timestamp: Utc::now(),
2053 metadata: HashMap::new(),
2054 signature: Some(self.compute_signature(content)),
2055 acknowledged_by: Vec::new(),
2056 status: MessageStatus::Sent,
2057 priority: MessagePriority::default(),
2058 reply_to: None,
2059 correlation_id: None,
2060 thread_id: None,
2061 comm_timestamp: ts,
2062 rich_content_json: None,
2063 comm_id: None,
2064 receipt_id: None,
2065 };
2066
2067 self.messages.insert(id, message.clone());
2068 delivered.push(message);
2069 }
2070
2071 Ok(delivered)
2072 }
2073
2074 pub fn dead_letter_count(&self) -> usize {
2080 self.dead_letters.len()
2081 }
2082
2083 pub fn list_dead_letters(&self) -> Vec<DeadLetter> {
2085 let mut dl = self.dead_letters.clone();
2086 dl.sort_by_key(|d| d.dead_lettered_at);
2087 dl
2088 }
2089
2090 pub fn replay_dead_letter(&mut self, index: usize) -> CommResult<Message> {
2096 if index >= self.dead_letters.len() {
2097 return Err(CommError::DeadLetterNotFound(index));
2098 }
2099
2100 let dl = self.dead_letters[index].clone();
2101 let orig = &dl.original_message;
2102
2103 match self.send_message(
2105 orig.channel_id,
2106 &orig.sender,
2107 &orig.content,
2108 orig.message_type,
2109 ) {
2110 Ok(msg) => {
2111 self.dead_letters.remove(index);
2113 Ok(msg)
2114 }
2115 Err(e) => {
2116 let new_len = self.dead_letters.len();
2120 if new_len > dl.retry_count as usize + self.dead_letters.len() {
2122 self.dead_letters.pop();
2124 }
2125 if index < self.dead_letters.len() {
2128 self.dead_letters[index].retry_count += 1;
2129 }
2130 Err(e)
2131 }
2132 }
2133 }
2134
2135 pub fn clear_dead_letters(&mut self) {
2137 self.dead_letters.clear();
2138 }
2139
2140 pub fn expire_messages(&mut self) -> usize {
2152 let now = Utc::now();
2153 let mut expired_ids: Vec<u64> = Vec::new();
2154
2155 for msg in self.messages.values() {
2156 if let Some(channel) = self.channels.get(&msg.channel_id) {
2157 if channel.config.ttl_seconds > 0 {
2158 let age = now
2159 .signed_duration_since(msg.timestamp)
2160 .num_seconds();
2161 if age > channel.config.ttl_seconds as i64 {
2162 expired_ids.push(msg.id);
2163 }
2164 }
2165 }
2166 }
2167
2168 let count = expired_ids.len();
2169
2170 for id in expired_ids {
2171 if let Some(mut msg) = self.messages.remove(&id) {
2172 msg.status = MessageStatus::Expired;
2173 self.dead_letters.push(DeadLetter {
2174 original_message: msg,
2175 reason: DeadLetterReason::Expired,
2176 dead_lettered_at: now,
2177 retry_count: 0,
2178 });
2179 }
2180 }
2181
2182 count
2183 }
2184
2185 pub fn compact(&mut self) -> usize {
2194 let mut removed = 0usize;
2195
2196 let closed_channel_ids: Vec<u64> = self
2198 .channels
2199 .values()
2200 .filter(|c| c.state == ChannelState::Closed)
2201 .map(|c| c.id)
2202 .collect();
2203
2204 let ids_to_remove: Vec<u64> = self
2205 .messages
2206 .values()
2207 .filter(|m| closed_channel_ids.contains(&m.channel_id))
2208 .map(|m| m.id)
2209 .collect();
2210
2211 for id in ids_to_remove {
2212 self.messages.remove(&id);
2213 removed += 1;
2214 }
2215
2216 for channel in self.channels.values() {
2218 if let RetentionPolicy::MessageCount(max_count) = channel.config.retention_policy {
2219 let mut channel_msgs: Vec<(u64, DateTime<Utc>)> = self
2220 .messages
2221 .values()
2222 .filter(|m| m.channel_id == channel.id)
2223 .map(|m| (m.id, m.timestamp))
2224 .collect();
2225
2226 if channel_msgs.len() > max_count as usize {
2227 channel_msgs.sort_by_key(|&(_, ts)| ts);
2229 let to_remove = channel_msgs.len() - max_count as usize;
2230 for (id, _) in channel_msgs.into_iter().take(to_remove) {
2231 self.messages.remove(&id);
2232 removed += 1;
2233 }
2234 }
2235 }
2236 }
2237
2238 removed
2239 }
2240
2241 pub fn query_history(&self, channel_id: u64, filter: &MessageFilter) -> Vec<Message> {
2247 let mut results: Vec<Message> = self
2248 .messages
2249 .values()
2250 .filter(|m| {
2251 if m.channel_id != channel_id {
2252 return false;
2253 }
2254 if let Some(ref since) = filter.since {
2255 if m.timestamp < *since {
2256 return false;
2257 }
2258 }
2259 if let Some(ref before) = filter.before {
2260 if m.timestamp > *before {
2261 return false;
2262 }
2263 }
2264 if let Some(ref sender) = filter.sender {
2265 if m.sender != *sender {
2266 return false;
2267 }
2268 }
2269 if let Some(ref msg_type) = filter.message_type {
2270 if m.message_type != *msg_type {
2271 return false;
2272 }
2273 }
2274 if let Some(priority_val) = filter.priority {
2275 let msg_priority = m.priority as u32;
2276 if msg_priority != priority_val {
2277 return false;
2278 }
2279 }
2280 if let Some(filter_thread) = filter.thread_id {
2281 match &m.thread_id {
2282 Some(tid) => {
2283 if let Ok(parsed) = tid.parse::<u64>() {
2284 if parsed != filter_thread {
2285 return false;
2286 }
2287 } else {
2288 return false;
2289 }
2290 }
2291 None => return false,
2292 }
2293 }
2294 if let Some(ref substr) = filter.content_contains {
2295 if !m.content.to_lowercase().contains(&substr.to_lowercase()) {
2296 return false;
2297 }
2298 }
2299 true
2300 })
2301 .cloned()
2302 .collect();
2303
2304 results.sort_by_key(|m| m.timestamp);
2305
2306 if let Some(limit) = filter.limit {
2307 results.truncate(limit);
2308 }
2309
2310 results
2311 }
2312
2313 pub fn search_messages(&self, query_text: &str, max_results: usize) -> Vec<Message> {
2315 let query_lower = query_text.to_lowercase();
2316 let mut results: Vec<Message> = self
2317 .messages
2318 .values()
2319 .filter(|m| m.content.to_lowercase().contains(&query_lower))
2320 .cloned()
2321 .collect();
2322
2323 results.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
2324 results.truncate(max_results);
2325 results
2326 }
2327
2328 pub fn get_message(&self, message_id: u64) -> Option<Message> {
2330 self.messages.get(&message_id).cloned()
2331 }
2332
2333 pub fn save(&self, path: &Path) -> CommResult<()> {
2348 CommFileLock::recover_stale(path, 60)?;
2350
2351 let _lock = CommFileLock::acquire(path)?;
2352
2353 let store_bytes =
2354 bincode::serialize(self).map_err(|e| CommError::Serialization(e.to_string()))?;
2355
2356 let compressed = zstd::bulk::compress(&store_bytes, 3)
2358 .map_err(|e| CommError::Serialization(format!("Zstd compression failed: {e}")))?;
2359
2360 let output = format::write_with_header_flags(&compressed, format::FLAG_ZSTD);
2363
2364 let mut file = std::fs::File::create(path)?;
2365 file.write_all(&output)?;
2366
2367 Ok(())
2369 }
2370
2371 pub fn load(path: &Path) -> CommResult<Self> {
2381 CommFileLock::recover_stale(path, 60)?;
2383
2384 let _lock = CommFileLock::acquire_shared(path)?;
2385
2386 let mut raw = Vec::new();
2387 {
2388 let mut file = std::fs::File::open(path)?;
2389 file.read_to_end(&mut raw)?;
2390 }
2391
2392 if format::is_new_format(&raw) {
2393 let (header, compressed_data) = format::read_with_header_and_meta(&raw)
2395 .map_err(|e| CommError::InvalidFile(e))?;
2396
2397 let decompressed = if header.is_zstd() {
2398 zstd::bulk::decompress(&compressed_data, 64 * 1024 * 1024)
2400 .map_err(|e| CommError::InvalidFile(
2401 format!("Zstd decompression failed: {e}"),
2402 ))?
2403 } else {
2404 let mut decoder = GzDecoder::new(&compressed_data[..]);
2406 let mut buf = Vec::new();
2407 decoder.read_to_end(&mut buf)?;
2408 buf
2409 };
2410
2411 let store: CommStore = bincode::deserialize(&decompressed)
2412 .map_err(|e| CommError::InvalidFile(format!("Bad store data: {e}")))?;
2413 Ok(store)
2414 } else {
2415 let mut decoder = GzDecoder::new(&raw[..]);
2417 let mut data = Vec::new();
2418 decoder.read_to_end(&mut data)?;
2419
2420 let header: AcommHeader = bincode::deserialize(&data)
2422 .map_err(|e| CommError::InvalidFile(format!("Bad header: {e}")))?;
2423
2424 if header.magic != *ACOMM_MAGIC {
2425 return Err(CommError::InvalidFile(
2426 "Invalid magic bytes — not an .acomm file".to_string(),
2427 ));
2428 }
2429
2430 if header.version != ACOMM_VERSION {
2431 return Err(CommError::InvalidFile(format!(
2432 "Unsupported version: {} (expected {})",
2433 header.version, ACOMM_VERSION
2434 )));
2435 }
2436
2437 let header_size = bincode::serialized_size(&header)
2439 .map_err(|e| CommError::Serialization(e.to_string()))? as usize;
2440
2441 let store: CommStore = bincode::deserialize(&data[header_size..])
2442 .map_err(|e| CommError::InvalidFile(format!("Bad store data: {e}")))?;
2443
2444 Ok(store)
2446 }
2447 }
2448
2449 pub fn stats(&self) -> CommStoreStats {
2451 let mut messages_by_type: HashMap<String, usize> = HashMap::new();
2453 let mut messages_by_priority: HashMap<String, usize> = HashMap::new();
2454 let mut oldest_message: Option<DateTime<Utc>> = None;
2455 let mut newest_message: Option<DateTime<Utc>> = None;
2456
2457 for msg in self.messages.values() {
2458 *messages_by_type
2459 .entry(msg.message_type.to_string())
2460 .or_insert(0) += 1;
2461 *messages_by_priority
2462 .entry(msg.priority.to_string())
2463 .or_insert(0) += 1;
2464
2465 match oldest_message {
2466 None => oldest_message = Some(msg.timestamp),
2467 Some(ref ts) if msg.timestamp < *ts => oldest_message = Some(msg.timestamp),
2468 _ => {}
2469 }
2470 match newest_message {
2471 None => newest_message = Some(msg.timestamp),
2472 Some(ref ts) if msg.timestamp > *ts => newest_message = Some(msg.timestamp),
2473 _ => {}
2474 }
2475 }
2476
2477 let mut channels_by_state: HashMap<String, usize> = HashMap::new();
2479 for ch in self.channels.values() {
2480 *channels_by_state
2481 .entry(ch.state.to_string())
2482 .or_insert(0) += 1;
2483 }
2484
2485 CommStoreStats {
2486 channel_count: self.channels.len(),
2487 message_count: self.messages.len(),
2488 subscription_count: self.subscriptions.len(),
2489 total_participants: self
2490 .channels
2491 .values()
2492 .map(|c| c.participants.len())
2493 .sum(),
2494 dead_letter_count: self.dead_letters.len(),
2495 messages_by_type,
2496 messages_by_priority,
2497 channels_by_state,
2498 oldest_message,
2499 newest_message,
2500 consent_gate_count: self.consent_gates.len(),
2501 trust_override_count: self.trust_levels.len(),
2502 temporal_queue_count: self.temporal_queue.iter().filter(|m| !m.delivered).count(),
2503 hive_count: self.hive_minds.len(),
2504 comm_log_count: self.comm_log.len(),
2505 federation_enabled: self.federation_config.enabled,
2506 federated_zone_count: self.federation_config.zones.len(),
2507 audit_log_count: self.audit_log.len(),
2508 }
2509 }
2510
2511 pub fn grant_consent(
2517 &mut self,
2518 grantor: &str,
2519 grantee: &str,
2520 scope: ConsentScope,
2521 reason: Option<String>,
2522 expires_at: Option<String>,
2523 ) -> CommResult<&ConsentGateEntry> {
2524 if grantor.is_empty() || grantee.is_empty() {
2525 return Err(CommError::ConsentError(
2526 "Grantor and grantee must be non-empty".to_string(),
2527 ));
2528 }
2529 let scope_str = scope.to_string();
2530 let now = Utc::now().to_rfc3339();
2531 if let Some(entry) = self.consent_gates.iter_mut().find(|e| {
2533 e.grantor == grantor && e.grantee == grantee && e.scope == scope
2534 }) {
2535 entry.status = ConsentStatus::Granted;
2536 entry.updated_at = now;
2537 entry.reason = reason;
2538 entry.expires_at = expires_at;
2539 self.audit_log.push(AuditEntry {
2541 event_type: AuditEventType::ConsentGranted,
2542 timestamp: Utc::now().to_rfc3339(),
2543 agent_id: grantor.to_string(),
2544 description: format!("Granted {} consent to '{}'", scope_str, grantee),
2545 related_id: Some(format!("{}->{}", grantor, grantee)),
2546 });
2547 let idx = self.consent_gates.iter().position(|e| {
2548 e.grantor == grantor && e.grantee == grantee && e.scope == scope
2549 }).unwrap();
2550 return Ok(&self.consent_gates[idx]);
2551 }
2552 let entry = ConsentGateEntry {
2554 grantor: grantor.to_string(),
2555 grantee: grantee.to_string(),
2556 scope,
2557 status: ConsentStatus::Granted,
2558 created_at: now.clone(),
2559 updated_at: now,
2560 expires_at,
2561 reason,
2562 };
2563 self.consent_gates.push(entry);
2564 self.audit_log.push(AuditEntry {
2566 event_type: AuditEventType::ConsentGranted,
2567 timestamp: Utc::now().to_rfc3339(),
2568 agent_id: grantor.to_string(),
2569 description: format!("Granted {} consent to '{}'", scope_str, grantee),
2570 related_id: Some(format!("{}->{}", grantor, grantee)),
2571 });
2572 Ok(self.consent_gates.last().unwrap())
2573 }
2574
2575 pub fn revoke_consent(
2577 &mut self,
2578 grantor: &str,
2579 grantee: &str,
2580 scope: &ConsentScope,
2581 ) -> CommResult<()> {
2582 if let Some(entry) = self.consent_gates.iter_mut().find(|e| {
2583 e.grantor == grantor && e.grantee == grantee && e.scope == *scope
2584 }) {
2585 entry.status = ConsentStatus::Revoked;
2586 entry.updated_at = Utc::now().to_rfc3339();
2587 self.log_audit(
2589 AuditEventType::ConsentRevoked,
2590 grantor,
2591 &format!("Revoked {} consent from '{}'", scope, grantee),
2592 Some(format!("{}->{}", grantor, grantee)),
2593 );
2594 Ok(())
2595 } else {
2596 Err(CommError::ConsentError(format!(
2597 "No consent entry found for {grantor} -> {grantee} ({scope})"
2598 )))
2599 }
2600 }
2601
2602 pub fn check_consent(
2604 &self,
2605 grantor: &str,
2606 grantee: &str,
2607 scope: &ConsentScope,
2608 ) -> bool {
2609 self.consent_gates.iter().any(|e| {
2610 e.grantor == grantor
2611 && e.grantee == grantee
2612 && e.scope == *scope
2613 && e.status == ConsentStatus::Granted
2614 })
2615 }
2616
2617 pub fn list_consent_gates(&self, agent: Option<&str>) -> Vec<&ConsentGateEntry> {
2619 self.consent_gates
2620 .iter()
2621 .filter(|e| {
2622 agent.map_or(true, |a| e.grantor == a || e.grantee == a)
2623 })
2624 .collect()
2625 }
2626
2627 pub fn set_trust_level(
2633 &mut self,
2634 agent_id: &str,
2635 level: CommTrustLevel,
2636 ) -> CommResult<()> {
2637 if agent_id.is_empty() {
2638 return Err(CommError::TrustError(
2639 "Agent ID must be non-empty".to_string(),
2640 ));
2641 }
2642 self.trust_levels.insert(agent_id.to_string(), level);
2643
2644 self.log_audit(
2646 AuditEventType::TrustUpdated,
2647 agent_id,
2648 &format!("Trust level set to {} for '{}'", level, agent_id),
2649 Some(agent_id.to_string()),
2650 );
2651
2652 Ok(())
2653 }
2654
2655 pub fn get_trust_level(&self, agent_id: &str) -> CommTrustLevel {
2657 self.trust_levels
2658 .get(agent_id)
2659 .copied()
2660 .unwrap_or(CommTrustLevel::Standard)
2661 }
2662
2663 pub fn list_trust_levels(&self) -> &HashMap<String, CommTrustLevel> {
2665 &self.trust_levels
2666 }
2667
2668 pub fn schedule_message(
2674 &mut self,
2675 channel_id: u64,
2676 sender: &str,
2677 content: &str,
2678 target: TemporalTarget,
2679 affect: Option<AffectState>,
2680 ) -> CommResult<&TemporalMessage> {
2681 if !self.channels.contains_key(&channel_id) {
2683 return Err(CommError::ChannelNotFound(channel_id));
2684 }
2685 Self::validate_sender(sender)?;
2686 Self::validate_content(content)?;
2687
2688 if !self.check_consent_for_action(sender, "temporal", ConsentScope::ScheduleMessages) {
2690 return Err(CommError::ConsentDenied {
2691 reason: "Consent not granted for scheduling messages".to_string(),
2692 });
2693 }
2694
2695 let id = self.next_temporal_id;
2696 self.next_temporal_id += 1;
2697
2698 let msg = TemporalMessage {
2699 id,
2700 channel_id,
2701 sender: sender.to_string(),
2702 content: content.to_string(),
2703 target,
2704 scheduled_at: Utc::now().to_rfc3339(),
2705 delivered: false,
2706 affect,
2707 };
2708 self.temporal_queue.push(msg);
2709
2710 self.audit_log.push(AuditEntry {
2714 event_type: AuditEventType::ScheduledMessage,
2715 timestamp: Utc::now().to_rfc3339(),
2716 agent_id: sender.to_string(),
2717 description: format!(
2718 "Scheduled message to channel {} (temporal_id={})",
2719 channel_id, id
2720 ),
2721 related_id: Some(id.to_string()),
2722 });
2723
2724 Ok(self.temporal_queue.last().unwrap())
2725 }
2726
2727 pub fn list_scheduled(&self) -> Vec<&TemporalMessage> {
2729 self.temporal_queue
2730 .iter()
2731 .filter(|m| !m.delivered)
2732 .collect()
2733 }
2734
2735 pub fn cancel_scheduled(&mut self, temporal_id: u64) -> CommResult<()> {
2737 if let Some(msg) = self.temporal_queue.iter_mut().find(|m| m.id == temporal_id) {
2738 if msg.delivered {
2739 return Err(CommError::TemporalError(
2740 "Cannot cancel already-delivered message".to_string(),
2741 ));
2742 }
2743 self.temporal_queue.retain(|m| m.id != temporal_id);
2744 Ok(())
2745 } else {
2746 Err(CommError::TemporalError(format!(
2747 "Scheduled message {temporal_id} not found"
2748 )))
2749 }
2750 }
2751
2752 pub fn deliver_pending_temporal(&mut self) -> usize {
2755 let mut delivered = 0;
2756 let mut to_deliver = Vec::new();
2757
2758 for msg in &self.temporal_queue {
2759 if msg.delivered {
2760 continue;
2761 }
2762 match &msg.target {
2763 TemporalTarget::Immediate => {
2764 to_deliver.push((msg.id, msg.channel_id, msg.sender.clone(), msg.content.clone()));
2765 }
2766 _ => {} }
2768 }
2769
2770 for (temporal_id, channel_id, sender, content) in to_deliver {
2771 if self.send_message(channel_id, &sender, &content, MessageType::Text).is_ok() {
2772 if let Some(msg) = self.temporal_queue.iter_mut().find(|m| m.id == temporal_id) {
2773 msg.delivered = true;
2774 }
2775 delivered += 1;
2776 }
2777 }
2778 delivered
2779 }
2780
2781 pub fn send_affect_message(
2787 &mut self,
2788 channel_id: u64,
2789 sender: &str,
2790 content: &str,
2791 affect: AffectState,
2792 ) -> CommResult<Message> {
2793 Self::validate_sender(sender)?;
2795 Self::validate_content(content)?;
2796 self.check_channel_allows_send(channel_id)?;
2797
2798 let affect_json = serde_json::to_string(&affect)
2800 .map_err(|e| CommError::Serialization(e.to_string()))?;
2801 let enriched = format!("[affect:{}]{}", affect_json, content);
2802
2803 self.send_message(channel_id, sender, &enriched, MessageType::Text)
2804 }
2805
2806 pub fn configure_federation(
2812 &mut self,
2813 enabled: bool,
2814 local_zone: &str,
2815 default_policy: FederationPolicy,
2816 ) -> CommResult<()> {
2817 if local_zone.is_empty() {
2818 return Err(CommError::FederationError(
2819 "Local zone must be non-empty".to_string(),
2820 ));
2821 }
2822
2823 if !self.check_consent_for_action("system", local_zone, ConsentScope::Federate) {
2827 return Err(CommError::ConsentDenied {
2828 reason: "Consent not granted for federation".to_string(),
2829 });
2830 }
2831
2832 self.federation_config.enabled = enabled;
2833 self.federation_config.local_zone = local_zone.to_string();
2834 self.federation_config.default_policy = default_policy;
2835
2836 self.log_audit(
2838 AuditEventType::FederationConfigured,
2839 "system",
2840 &format!(
2841 "Federation configured: enabled={}, zone='{}', policy={}",
2842 enabled, local_zone, default_policy
2843 ),
2844 Some(local_zone.to_string()),
2845 );
2846
2847 Ok(())
2848 }
2849
2850 pub fn get_federation_config(&self) -> &FederationConfig {
2852 &self.federation_config
2853 }
2854
2855 pub fn add_federated_zone(&mut self, zone: FederatedZone) -> CommResult<()> {
2857 if zone.zone_id.is_empty() {
2858 return Err(CommError::FederationError(
2859 "Zone ID must be non-empty".to_string(),
2860 ));
2861 }
2862 if self.federation_config.zones.iter().any(|z| z.zone_id == zone.zone_id) {
2864 return Err(CommError::FederationError(format!(
2865 "Zone '{}' already exists", zone.zone_id
2866 )));
2867 }
2868 self.federation_config.zones.push(zone);
2869 Ok(())
2870 }
2871
2872 pub fn remove_federated_zone(&mut self, zone_id: &str) -> CommResult<()> {
2874 let before = self.federation_config.zones.len();
2875 self.federation_config.zones.retain(|z| z.zone_id != zone_id);
2876 if self.federation_config.zones.len() == before {
2877 return Err(CommError::FederationError(format!(
2878 "Zone '{zone_id}' not found"
2879 )));
2880 }
2881 Ok(())
2882 }
2883
2884 pub fn list_federated_zones(&self) -> &[FederatedZone] {
2886 &self.federation_config.zones
2887 }
2888
2889 pub fn form_hive(
2895 &mut self,
2896 name: &str,
2897 coordinator: &str,
2898 decision_mode: CollectiveDecisionMode,
2899 ) -> CommResult<&HiveMind> {
2900 if name.is_empty() {
2901 return Err(CommError::HiveError(
2902 "Hive name must be non-empty".to_string(),
2903 ));
2904 }
2905 if coordinator.is_empty() {
2906 return Err(CommError::HiveError(
2907 "Coordinator must be non-empty".to_string(),
2908 ));
2909 }
2910
2911 if !self.check_consent_for_action(coordinator, name, ConsentScope::HiveParticipation) {
2913 return Err(CommError::ConsentDenied {
2914 reason: "Consent not granted for hive participation".to_string(),
2915 });
2916 }
2917
2918 let id = self.next_hive_id;
2919 self.next_hive_id += 1;
2920 let now = Utc::now().to_rfc3339();
2921 let hive = HiveMind {
2922 id,
2923 name: name.to_string(),
2924 constituents: vec![HiveConstituent {
2925 agent_id: coordinator.to_string(),
2926 role: HiveRole::Coordinator,
2927 joined_at: now.clone(),
2928 }],
2929 decision_mode,
2930 formed_at: now,
2931 metadata: HashMap::new(),
2932 coherence_level: 1.0,
2933 separation_policy: "graceful".to_string(),
2934 cognitive_space: None,
2935 };
2936 self.hive_minds.insert(id, hive);
2937
2938 self.audit_log.push(AuditEntry {
2942 event_type: AuditEventType::HiveFormed,
2943 timestamp: Utc::now().to_rfc3339(),
2944 agent_id: coordinator.to_string(),
2945 description: format!("Formed hive '{}' (id={})", name, id),
2946 related_id: Some(id.to_string()),
2947 });
2948
2949 Ok(self.hive_minds.get(&id).unwrap())
2950 }
2951
2952 pub fn dissolve_hive(&mut self, hive_id: u64) -> CommResult<()> {
2954 if self.hive_minds.remove(&hive_id).is_none() {
2955 return Err(CommError::HiveError(format!(
2956 "Hive {hive_id} not found"
2957 )));
2958 }
2959
2960 self.log_audit(
2962 AuditEventType::HiveDissolved,
2963 "system",
2964 &format!("Dissolved hive (id={})", hive_id),
2965 Some(hive_id.to_string()),
2966 );
2967
2968 Ok(())
2969 }
2970
2971 pub fn join_hive(
2973 &mut self,
2974 hive_id: u64,
2975 agent_id: &str,
2976 role: HiveRole,
2977 ) -> CommResult<()> {
2978 {
2980 let hive_name = self
2981 .hive_minds
2982 .get(&hive_id)
2983 .map(|h| h.name.clone())
2984 .unwrap_or_default();
2985 if !self.check_consent_for_action(agent_id, &hive_name, ConsentScope::HiveParticipation) {
2986 return Err(CommError::ConsentDenied {
2987 reason: "Consent not granted for hive participation".to_string(),
2988 });
2989 }
2990 }
2991
2992 let hive = self
2993 .hive_minds
2994 .get_mut(&hive_id)
2995 .ok_or_else(|| CommError::HiveError(format!("Hive {hive_id} not found")))?;
2996
2997 if hive.constituents.iter().any(|c| c.agent_id == agent_id) {
2998 return Err(CommError::HiveError(format!(
2999 "Agent '{agent_id}' is already in hive {hive_id}"
3000 )));
3001 }
3002
3003 hive.constituents.push(HiveConstituent {
3004 agent_id: agent_id.to_string(),
3005 role,
3006 joined_at: Utc::now().to_rfc3339(),
3007 });
3008 Ok(())
3009 }
3010
3011 pub fn leave_hive(&mut self, hive_id: u64, agent_id: &str) -> CommResult<()> {
3013 let hive = self
3014 .hive_minds
3015 .get_mut(&hive_id)
3016 .ok_or_else(|| CommError::HiveError(format!("Hive {hive_id} not found")))?;
3017
3018 let before = hive.constituents.len();
3019 hive.constituents.retain(|c| c.agent_id != agent_id);
3020 if hive.constituents.len() == before {
3021 return Err(CommError::HiveError(format!(
3022 "Agent '{agent_id}' is not in hive {hive_id}"
3023 )));
3024 }
3025 Ok(())
3026 }
3027
3028 pub fn list_hives(&self) -> Vec<&HiveMind> {
3030 self.hive_minds.values().collect()
3031 }
3032
3033 pub fn get_hive(&self, hive_id: u64) -> Option<&HiveMind> {
3035 self.hive_minds.get(&hive_id)
3036 }
3037
3038 pub fn log_communication(
3044 &mut self,
3045 content: &str,
3046 role: &str,
3047 topic: Option<String>,
3048 linked_message_id: Option<u64>,
3049 affect: Option<AffectState>,
3050 ) -> &CommunicationLogEntry {
3051 let idx = self.next_log_index;
3052 self.next_log_index += 1;
3053 let entry = CommunicationLogEntry {
3054 index: idx,
3055 content: content.to_string(),
3056 role: role.to_string(),
3057 topic,
3058 timestamp: Utc::now().to_rfc3339(),
3059 linked_message_id,
3060 affect,
3061 };
3062 self.comm_log.push(entry);
3063 self.comm_log.last().unwrap()
3064 }
3065
3066 pub fn get_comm_log(&self, limit: Option<usize>) -> &[CommunicationLogEntry] {
3068 match limit {
3069 Some(n) if n < self.comm_log.len() => &self.comm_log[self.comm_log.len() - n..],
3070 _ => &self.comm_log,
3071 }
3072 }
3073
3074 pub fn log_audit(
3080 &mut self,
3081 event_type: AuditEventType,
3082 agent_id: &str,
3083 description: &str,
3084 related_id: Option<String>,
3085 ) {
3086 let entry = AuditEntry {
3087 event_type,
3088 timestamp: Utc::now().to_rfc3339(),
3089 agent_id: agent_id.to_string(),
3090 description: description.to_string(),
3091 related_id,
3092 };
3093 self.audit_log.push(entry);
3094 }
3095
3096 pub fn get_audit_log(&self, limit: Option<usize>) -> Vec<&AuditEntry> {
3098 match limit {
3099 Some(n) if n < self.audit_log.len() => {
3100 self.audit_log[self.audit_log.len() - n..].iter().collect()
3101 }
3102 _ => self.audit_log.iter().collect(),
3103 }
3104 }
3105
3106 pub fn rotate_audit_log(&mut self, max_entries: usize) -> usize {
3108 if self.audit_log.len() <= max_entries {
3109 return 0;
3110 }
3111 let removed = self.audit_log.len() - max_entries;
3112 self.audit_log = self.audit_log.split_off(removed);
3113 removed
3114 }
3115
3116 pub fn enforce_audit_retention(&mut self, cutoff_timestamp: &str) -> usize {
3118 let before = self.audit_log.len();
3119 self.audit_log.retain(|entry| entry.timestamp.as_str() >= cutoff_timestamp);
3120 before - self.audit_log.len()
3121 }
3122
3123 pub fn export_audit_log(&self) -> serde_json::Value {
3125 let entries: Vec<serde_json::Value> = self.audit_log.iter().map(|e| {
3126 serde_json::json!({
3127 "event_type": format!("{:?}", e.event_type),
3128 "timestamp": e.timestamp,
3129 "agent_id": e.agent_id,
3130 "description": e.description,
3131 "related_id": e.related_id,
3132 })
3133 }).collect();
3134 serde_json::json!({
3135 "total_entries": entries.len(),
3136 "entries": entries,
3137 })
3138 }
3139
3140 pub fn send_semantic(
3146 &mut self,
3147 channel_id: u64,
3148 sender: &str,
3149 topic: &str,
3150 focus_nodes: Vec<String>,
3151 depth: u64,
3152 ) -> CommResult<SemanticOperation> {
3153 if !self.channels.contains_key(&channel_id) {
3155 return Err(CommError::ChannelNotFound(channel_id));
3156 }
3157 let id = self.next_semantic_id;
3158 self.next_semantic_id += 1;
3159 let op = SemanticOperation {
3160 id,
3161 topic: topic.to_string(),
3162 focus_nodes,
3163 depth,
3164 timestamp: Utc::now().timestamp() as u64,
3165 operation: "send".to_string(),
3166 channel_id: Some(channel_id),
3167 sender: Some(sender.to_string()),
3168 };
3169 self.semantic_operations.push(op.clone());
3170 Ok(op)
3171 }
3172
3173 pub fn extract_semantic(&self, message_id: u64) -> CommResult<SemanticOperation> {
3175 let msg = self
3176 .messages
3177 .get(&message_id)
3178 .ok_or(CommError::MessageNotFound(message_id))?;
3179 Ok(SemanticOperation {
3180 id: 0,
3181 topic: String::new(),
3182 focus_nodes: vec![],
3183 depth: 1,
3184 timestamp: Utc::now().timestamp() as u64,
3185 operation: "extract".to_string(),
3186 channel_id: Some(msg.channel_id),
3187 sender: Some(msg.sender.clone()),
3188 })
3189 }
3190
3191 pub fn graft_semantic(
3193 &mut self,
3194 source_id: u64,
3195 target_id: u64,
3196 strategy: &str,
3197 ) -> CommResult<SemanticOperation> {
3198 let _ = (source_id, target_id, strategy);
3199 let id = self.next_semantic_id;
3200 self.next_semantic_id += 1;
3201 let op = SemanticOperation {
3202 id,
3203 topic: String::new(),
3204 focus_nodes: vec![],
3205 depth: 1,
3206 timestamp: Utc::now().timestamp() as u64,
3207 operation: format!("graft:{}->{}:{}", source_id, target_id, strategy),
3208 channel_id: None,
3209 sender: None,
3210 };
3211 self.semantic_operations.push(op.clone());
3212 Ok(op)
3213 }
3214
3215 pub fn list_semantic_conflicts(
3217 &self,
3218 channel_id: Option<u64>,
3219 severity: Option<&str>,
3220 ) -> Vec<&SemanticConflict> {
3221 self.semantic_conflicts
3222 .iter()
3223 .filter(|c| {
3224 channel_id.map_or(true, |cid| c.channel_id == Some(cid))
3225 && severity.map_or(true, |s| c.severity == s)
3226 })
3227 .collect()
3228 }
3229
3230 pub fn get_affect_state(&self, agent_id: &str) -> Option<&AffectState> {
3236 self.affect_states.get(agent_id)
3237 }
3238
3239 pub fn set_affect_resistance(&mut self, resistance: f64) -> f64 {
3241 let clamped = resistance.clamp(0.0, 1.0);
3242 self.affect_resistance = clamped;
3243 clamped
3244 }
3245
3246
3247 pub fn process_affect_contagion(
3257 &mut self,
3258 channel_id: u64,
3259 ) -> Vec<(String, f64, f64, f64)> {
3260 let channel = match self.channels.get(&channel_id) {
3261 Some(ch) => ch.clone(),
3262 None => return Vec::new(),
3263 };
3264 let participants = channel.participants.clone();
3265 let resistance = self.affect_resistance;
3266
3267 let mut affect_messages: Vec<(String, f64, f64, f64)> = Vec::new();
3269 for msg in self.messages.values() {
3270 if msg.channel_id != channel_id {
3271 continue;
3272 }
3273 let valence = msg
3274 .metadata
3275 .get("valence")
3276 .and_then(|v| v.parse::<f64>().ok());
3277 let arousal = msg
3278 .metadata
3279 .get("arousal")
3280 .and_then(|v| v.parse::<f64>().ok());
3281 let dominance = msg
3282 .metadata
3283 .get("dominance")
3284 .and_then(|v| v.parse::<f64>().ok());
3285
3286 if let (Some(v), Some(a), Some(d)) = (valence, arousal, dominance) {
3287 affect_messages.push((msg.sender.clone(), v, a, d));
3288 }
3289 }
3290
3291 let mut results: Vec<(String, f64, f64, f64)> = Vec::new();
3292
3293 for (sender, v, a, d) in &affect_messages {
3294 for participant in &participants {
3295 if participant == sender {
3296 continue;
3297 }
3298 let weight = 1.0 - resistance;
3299 let current = self
3300 .affect_states
3301 .get(participant)
3302 .cloned()
3303 .unwrap_or_default();
3304 let new_valence =
3305 (current.valence + (v - current.valence) * weight).clamp(-1.0, 1.0);
3306 let new_arousal =
3307 (current.arousal + (a - current.arousal) * weight).clamp(0.0, 1.0);
3308 let new_dominance =
3309 (current.dominance + (d - current.dominance) * weight).clamp(0.0, 1.0);
3310
3311 let state = self
3312 .affect_states
3313 .entry(participant.clone())
3314 .or_insert_with(AffectState::default);
3315 state.valence = new_valence;
3316 state.arousal = new_arousal;
3317 state.dominance = new_dominance;
3318
3319 results.push((
3320 participant.clone(),
3321 new_valence,
3322 new_arousal,
3323 new_dominance,
3324 ));
3325 }
3326 }
3327
3328 results
3329 }
3330
3331 pub fn get_affect_history(&self, agent: &str) -> types::AffectHistory {
3336 use crate::types::{AffectHistory, AffectHistoryEntry};
3337
3338 let mut entries: Vec<AffectHistoryEntry> = Vec::new();
3339
3340 for msg in self.messages.values() {
3342 let involves_agent = msg.sender == agent
3343 || msg
3344 .recipient
3345 .as_deref()
3346 .map_or(false, |r| r == agent);
3347
3348 if !involves_agent {
3349 continue;
3350 }
3351
3352 let valence = msg
3353 .metadata
3354 .get("valence")
3355 .and_then(|v| v.parse::<f64>().ok());
3356 let arousal = msg
3357 .metadata
3358 .get("arousal")
3359 .and_then(|v| v.parse::<f64>().ok());
3360 let dominance = msg
3361 .metadata
3362 .get("dominance")
3363 .and_then(|v| v.parse::<f64>().ok());
3364
3365 if valence.is_some() || arousal.is_some() || dominance.is_some() {
3366 entries.push(AffectHistoryEntry {
3367 timestamp: msg.timestamp.timestamp() as u64,
3368 emotion: String::new(),
3369 intensity: 0.0,
3370 valence: valence.unwrap_or(0.0),
3371 arousal: arousal.unwrap_or(0.0),
3372 dominance: dominance.unwrap_or(0.5),
3373 source: if msg.sender == agent {
3374 "direct".to_string()
3375 } else {
3376 "contagion".to_string()
3377 },
3378 });
3379 }
3380 }
3381
3382 if let Some(state) = self.affect_states.get(agent) {
3384 entries.push(AffectHistoryEntry {
3385 timestamp: chrono::Utc::now().timestamp() as u64,
3386 emotion: String::new(),
3387 intensity: 0.0,
3388 valence: state.valence,
3389 arousal: state.arousal,
3390 dominance: state.dominance,
3391 source: "current".to_string(),
3392 });
3393 }
3394
3395 entries.sort_by_key(|e| e.timestamp);
3396
3397 AffectHistory {
3398 agent: agent.to_string(),
3399 states: entries,
3400 }
3401 }
3402
3403 pub fn apply_affect_decay(&mut self, decay_rate: f64) {
3409 let factor = 1.0 - decay_rate.clamp(0.0, 1.0);
3410 for state in self.affect_states.values_mut() {
3411 state.valence = (state.valence * factor).clamp(-1.0, 1.0);
3412 state.arousal = (state.arousal * factor).clamp(0.0, 1.0);
3413 state.dominance = (state.dominance * factor).clamp(0.0, 1.0);
3414 }
3415 }
3416
3417 pub fn forward_message(
3426 &mut self,
3427 original_id: u64,
3428 target_channel: u64,
3429 forwarder: &str,
3430 ) -> Result<u64, String> {
3431 let original = self
3432 .messages
3433 .get(&original_id)
3434 .cloned()
3435 .ok_or_else(|| format!("Message {} not found", original_id))?;
3436
3437 if !self.channels.contains_key(&target_channel) {
3438 return Err(format!("Target channel {} not found", target_channel));
3439 }
3440
3441 let parent_depth: u32 = original
3443 .metadata
3444 .get("echo_depth")
3445 .and_then(|v| v.parse().ok())
3446 .unwrap_or(0);
3447 let root_id = original
3448 .metadata
3449 .get("original_message_id")
3450 .and_then(|v| v.parse::<u64>().ok())
3451 .unwrap_or(original_id);
3452
3453 let content = format!("[Forwarded] {}", original.content);
3454
3455 let id = self.next_message_id;
3456 self.next_message_id += 1;
3457
3458 self.lamport_counter += 1;
3459 let mut ts = types::CommTimestamp::now(forwarder);
3460 ts.lamport = self.lamport_counter;
3461
3462 let mut metadata = HashMap::new();
3463 metadata.insert("forwarded_from".to_string(), original_id.to_string());
3464 metadata.insert("echo_depth".to_string(), (parent_depth + 1).to_string());
3465 metadata.insert("original_message_id".to_string(), root_id.to_string());
3466 metadata.insert("forwarder".to_string(), forwarder.to_string());
3467
3468 let message = Message {
3469 id,
3470 channel_id: target_channel,
3471 sender: forwarder.to_string(),
3472 recipient: None,
3473 content,
3474 message_type: MessageType::Text,
3475 timestamp: Utc::now(),
3476 metadata,
3477 signature: Some(self.compute_signature(&original.content)),
3478 acknowledged_by: Vec::new(),
3479 status: MessageStatus::Sent,
3480 priority: MessagePriority::default(),
3481 reply_to: None,
3482 correlation_id: None,
3483 thread_id: None,
3484 comm_timestamp: ts,
3485 rich_content_json: None,
3486 comm_id: None,
3487 receipt_id: None,
3488 };
3489
3490 self.messages.insert(id, message);
3491
3492 self.log_audit(
3493 AuditEventType::MessageSent,
3494 forwarder,
3495 &format!(
3496 "Forwarded message {} to channel {} (depth {})",
3497 original_id,
3498 target_channel,
3499 parent_depth + 1
3500 ),
3501 Some(id.to_string()),
3502 );
3503
3504 Ok(id)
3505 }
3506
3507 pub fn query_echo_chain(&self, message_id: u64) -> Vec<types::EchoChainEntry> {
3513 use crate::types::EchoChainEntry;
3514
3515 let mut chain: Vec<EchoChainEntry> = Vec::new();
3516
3517 let mut current_id = message_id;
3519 let mut visited: std::collections::HashSet<u64> = std::collections::HashSet::new();
3520 loop {
3521 if !visited.insert(current_id) {
3522 break; }
3524 let msg = match self.messages.get(¤t_id) {
3525 Some(m) => m,
3526 None => break,
3527 };
3528 let depth: u32 = msg
3529 .metadata
3530 .get("echo_depth")
3531 .and_then(|v| v.parse().ok())
3532 .unwrap_or(0);
3533 let forwarder = msg
3534 .metadata
3535 .get("forwarder")
3536 .cloned()
3537 .unwrap_or_else(|| msg.sender.clone());
3538
3539 chain.push(EchoChainEntry {
3540 message_id: current_id,
3541 channel_id: msg.channel_id,
3542 sender: msg.sender.clone(),
3543 forwarder,
3544 depth,
3545 timestamp: msg.timestamp.timestamp() as u64,
3546 });
3547
3548 match msg
3549 .metadata
3550 .get("forwarded_from")
3551 .and_then(|v| v.parse::<u64>().ok())
3552 {
3553 Some(parent) => current_id = parent,
3554 None => break,
3555 }
3556 }
3557
3558 chain.reverse(); let chain_ids: std::collections::HashSet<u64> =
3562 chain.iter().map(|e| e.message_id).collect();
3563 for msg in self.messages.values() {
3564 if chain_ids.contains(&msg.id) {
3565 continue; }
3567 if let Some(parent_str) = msg.metadata.get("forwarded_from") {
3568 if let Ok(parent_id) = parent_str.parse::<u64>() {
3569 if chain_ids.contains(&parent_id) {
3570 let depth: u32 = msg
3571 .metadata
3572 .get("echo_depth")
3573 .and_then(|v| v.parse().ok())
3574 .unwrap_or(0);
3575 let forwarder = msg
3576 .metadata
3577 .get("forwarder")
3578 .cloned()
3579 .unwrap_or_else(|| msg.sender.clone());
3580 chain.push(EchoChainEntry {
3581 message_id: msg.id,
3582 channel_id: msg.channel_id,
3583 sender: msg.sender.clone(),
3584 forwarder,
3585 depth,
3586 timestamp: msg.timestamp.timestamp() as u64,
3587 });
3588 }
3589 }
3590 }
3591 }
3592
3593 chain.sort_by_key(|e| (e.depth, e.timestamp));
3594 chain
3595 }
3596
3597 pub fn get_echo_depth(&self, message_id: u64) -> u32 {
3602 self.messages
3603 .get(&message_id)
3604 .and_then(|msg| {
3605 msg.metadata
3606 .get("echo_depth")
3607 .and_then(|v| v.parse().ok())
3608 })
3609 .unwrap_or(0)
3610 }
3611
3612 pub fn summarize_conversation(
3618 &self,
3619 channel_id: u64,
3620 ) -> Result<types::ConversationSummaryDetailed, String> {
3621 use crate::types::ConversationSummaryDetailed;
3622
3623 let channel = self
3624 .channels
3625 .get(&channel_id)
3626 .ok_or_else(|| format!("Channel {} not found", channel_id))?;
3627
3628 let msgs: Vec<&Message> = self
3629 .messages
3630 .values()
3631 .filter(|m| m.channel_id == channel_id)
3632 .collect();
3633
3634 let message_count = msgs.len();
3635 let participants = channel.participants.clone();
3636 let participant_count = participants.len();
3637
3638 let first_message_time = msgs
3639 .iter()
3640 .map(|m| m.timestamp.timestamp() as u64)
3641 .min();
3642 let last_message_time = msgs
3643 .iter()
3644 .map(|m| m.timestamp.timestamp() as u64)
3645 .max();
3646
3647 let duration_secs = match (first_message_time, last_message_time) {
3648 (Some(first), Some(last)) if last > first => last - first,
3649 _ => 0,
3650 };
3651
3652 let messages_per_minute = if duration_secs > 0 {
3653 (message_count as f64) / (duration_secs as f64 / 60.0)
3654 } else if message_count > 0 {
3655 message_count as f64
3656 } else {
3657 0.0
3658 };
3659
3660 let mut sender_counts: HashMap<String, usize> = HashMap::new();
3662 for msg in &msgs {
3663 *sender_counts
3664 .entry(msg.sender.clone())
3665 .or_insert(0) += 1;
3666 }
3667 let mut top_senders: Vec<(String, usize)> = sender_counts.into_iter().collect();
3668 top_senders.sort_by(|a, b| b.1.cmp(&a.1));
3669
3670 let most_active_participant = top_senders.first().map(|(name, _)| name.clone());
3671 let most_active_count = top_senders.first().map(|(_, c)| *c).unwrap_or(0);
3672
3673 let thread_ids: std::collections::HashSet<&str> = msgs
3675 .iter()
3676 .filter_map(|m| m.thread_id.as_deref())
3677 .collect();
3678 let thread_count = thread_ids.len();
3679
3680 let reply_count = msgs.iter().filter(|m| m.reply_to.is_some()).count();
3682
3683 let avg_message_length = if message_count > 0 {
3685 msgs.iter().map(|m| m.content.len()).sum::<usize>() as f64 / message_count as f64
3686 } else {
3687 0.0
3688 };
3689
3690 let has_affect_data = msgs.iter().any(|m| m.metadata.contains_key("valence"));
3692
3693 Ok(ConversationSummaryDetailed {
3694 channel_id,
3695 channel_name: channel.name.clone(),
3696 participant_count,
3697 message_count,
3698 participants,
3699 first_message_time,
3700 last_message_time,
3701 duration_secs,
3702 messages_per_minute,
3703 top_senders,
3704 most_active_participant,
3705 most_active_count,
3706 avg_message_length,
3707 thread_count,
3708 reply_count,
3709 has_affect_data,
3710 })
3711 }
3712
3713 pub fn hive_think(
3719 &self,
3720 hive_id: u64,
3721 question: &str,
3722 timeout_ms: u64,
3723 ) -> CommResult<serde_json::Value> {
3724 let hive = self
3725 .hive_minds
3726 .get(&hive_id)
3727 .ok_or_else(|| CommError::HiveError(format!("Hive {hive_id} not found")))?;
3728 Ok(serde_json::json!({
3729 "hive_id": hive_id,
3730 "hive_name": hive.name,
3731 "question": question,
3732 "timeout_ms": timeout_ms,
3733 "members": hive.constituents.len(),
3734 "status": "thought_broadcast",
3735 }))
3736 }
3737
3738 pub fn initiate_meld(
3740 &mut self,
3741 partner_id: &str,
3742 depth: &str,
3743 duration_ms: u64,
3744 ) -> MeldSession {
3745 let id = format!("meld-{}", Utc::now().timestamp_millis());
3746 let session = MeldSession {
3747 id: id.clone(),
3748 partner_id: partner_id.to_string(),
3749 depth: depth.to_string(),
3750 start_time: Utc::now().timestamp() as u64,
3751 duration_ms,
3752 active: true,
3753 };
3754 self.meld_sessions.push(session.clone());
3755 session
3756 }
3757
3758 pub fn list_pending_consent(
3764 &self,
3765 agent_id: Option<&str>,
3766 consent_type: Option<&str>,
3767 ) -> Vec<&ConsentRequest> {
3768 self.pending_consent_requests
3769 .iter()
3770 .filter(|r| {
3771 !r.responded
3772 && agent_id.map_or(true, |a| r.to == a || r.from == a)
3773 && consent_type.map_or(true, |ct| r.consent_type == ct)
3774 })
3775 .collect()
3776 }
3777
3778 pub fn respond_consent(
3780 &mut self,
3781 request_id: &str,
3782 response: &str,
3783 ) -> CommResult<()> {
3784 let req = self
3785 .pending_consent_requests
3786 .iter_mut()
3787 .find(|r| r.id == request_id)
3788 .ok_or_else(|| {
3789 CommError::ConsentError(format!("Consent request '{request_id}' not found"))
3790 })?;
3791 if req.responded {
3792 return Err(CommError::ConsentError(format!(
3793 "Consent request '{request_id}' already responded"
3794 )));
3795 }
3796 req.responded = true;
3797 req.response = Some(response.to_string());
3798 Ok(())
3799 }
3800
3801 pub fn query_relationships(
3807 &self,
3808 agent_id: &str,
3809 relationship_type: Option<&str>,
3810 depth: u64,
3811 ) -> serde_json::Value {
3812 let _ = depth;
3813 let mut relationships = Vec::new();
3814
3815 if relationship_type.is_none() || relationship_type == Some("trust") {
3817 if let Some(level) = self.trust_levels.get(agent_id) {
3818 relationships.push(serde_json::json!({
3819 "type": "trust",
3820 "agent": agent_id,
3821 "level": level.to_string(),
3822 }));
3823 }
3824 for (other, level) in &self.trust_levels {
3826 if other != agent_id {
3827 relationships.push(serde_json::json!({
3828 "type": "trusted_by",
3829 "agent": other,
3830 "level": level.to_string(),
3831 }));
3832 }
3833 }
3834 }
3835
3836 if relationship_type.is_none() || relationship_type == Some("channel") {
3838 for channel in self.channels.values() {
3839 if channel.participants.contains(&agent_id.to_string()) {
3840 for peer in &channel.participants {
3841 if peer != agent_id {
3842 relationships.push(serde_json::json!({
3843 "type": "channel_peer",
3844 "agent": peer,
3845 "channel_id": channel.id,
3846 "channel_name": channel.name,
3847 }));
3848 }
3849 }
3850 }
3851 }
3852 }
3853
3854 if relationship_type.is_none() || relationship_type == Some("consent") {
3856 for gate in &self.consent_gates {
3857 if gate.grantor == agent_id {
3858 relationships.push(serde_json::json!({
3859 "type": "consent_granted_to",
3860 "agent": gate.grantee,
3861 "scope": format!("{:?}", gate.scope),
3862 "status": format!("{:?}", gate.status),
3863 }));
3864 }
3865 if gate.grantee == agent_id {
3866 relationships.push(serde_json::json!({
3867 "type": "consent_received_from",
3868 "agent": gate.grantor,
3869 "scope": format!("{:?}", gate.scope),
3870 "status": format!("{:?}", gate.status),
3871 }));
3872 }
3873 }
3874 }
3875
3876 if relationship_type.is_none() || relationship_type == Some("hive") {
3878 for hive in self.hive_minds.values() {
3879 let is_member = hive.constituents.iter().any(|c| c.agent_id == agent_id);
3880 if is_member {
3881 for constituent in &hive.constituents {
3882 if constituent.agent_id != agent_id {
3883 relationships.push(serde_json::json!({
3884 "type": "hive_peer",
3885 "agent": constituent.agent_id,
3886 "hive_id": hive.id,
3887 "hive_name": hive.name,
3888 "role": format!("{:?}", constituent.role),
3889 }));
3890 }
3891 }
3892 }
3893 }
3894 }
3895
3896 serde_json::json!({
3897 "agent_id": agent_id,
3898 "relationship_count": relationships.len(),
3899 "relationships": relationships,
3900 })
3901 }
3902
3903 pub fn conversation_at_time(&self, channel_id: u64, timestamp: u64) -> serde_json::Value {
3905 let messages: Vec<&Message> = self.messages.values()
3906 .filter(|m| m.channel_id == channel_id && m.timestamp.timestamp() as u64 <= timestamp)
3907 .collect();
3908 let channel = self.channels.get(&channel_id);
3909 serde_json::json!({
3910 "channel_id": channel_id,
3911 "channel_name": channel.map(|c| c.name.as_str()).unwrap_or("unknown"),
3912 "as_of": timestamp,
3913 "message_count": messages.len(),
3914 "participants": channel.map(|c| &c.participants),
3915 "last_message": messages.last().map(|m| serde_json::json!({
3916 "id": m.id,
3917 "sender": m.sender,
3918 "content": m.content,
3919 "timestamp": m.timestamp.to_rfc3339(),
3920 })),
3921 })
3922 }
3923
3924 pub fn changes_in_range(&self, channel_id: u64, start: u64, end: u64) -> serde_json::Value {
3926 let messages: Vec<&Message> = self.messages.values()
3927 .filter(|m| {
3928 m.channel_id == channel_id
3929 && m.timestamp.timestamp() as u64 >= start
3930 && m.timestamp.timestamp() as u64 <= end
3931 })
3932 .collect();
3933 let new_participants: Vec<String> = Vec::new(); serde_json::json!({
3935 "channel_id": channel_id,
3936 "start": start,
3937 "end": end,
3938 "messages_added": messages.len(),
3939 "senders": messages.iter().map(|m| m.sender.clone()).collect::<std::collections::HashSet<_>>(),
3940 "message_types": messages.iter().map(|m| format!("{:?}", m.message_type)).collect::<std::collections::HashSet<_>>(),
3941 "new_participants": new_participants,
3942 })
3943 }
3944
3945 pub fn query_echoes(
3947 &self,
3948 message_id: u64,
3949 depth: u64,
3950 ) -> CommResult<serde_json::Value> {
3951 let msg = self
3952 .messages
3953 .get(&message_id)
3954 .ok_or(CommError::MessageNotFound(message_id))?;
3955 let _ = depth;
3956 let echoes: Vec<serde_json::Value> = self
3958 .messages
3959 .values()
3960 .filter(|m| m.channel_id == msg.channel_id && m.id != message_id)
3961 .take(50)
3962 .map(|m| {
3963 serde_json::json!({
3964 "message_id": m.id,
3965 "sender": m.sender,
3966 "channel_id": m.channel_id,
3967 "timestamp": m.timestamp.to_rfc3339(),
3968 })
3969 })
3970 .collect();
3971 Ok(serde_json::json!({
3972 "source_message_id": message_id,
3973 "echo_count": echoes.len(),
3974 "echoes": echoes,
3975 }))
3976 }
3977
3978 pub fn query_conversations(
3980 &self,
3981 channel_id: Option<u64>,
3982 participant: Option<&str>,
3983 limit: u64,
3984 ) -> Vec<ConversationSummary> {
3985 let mut summaries: Vec<ConversationSummary> = self
3986 .channels
3987 .values()
3988 .filter(|ch| {
3989 channel_id.map_or(true, |cid| ch.id == cid)
3990 && participant.map_or(true, |p| ch.participants.contains(&p.to_string()))
3991 })
3992 .map(|ch| {
3993 let msg_count = self
3994 .messages
3995 .values()
3996 .filter(|m| m.channel_id == ch.id)
3997 .count() as u64;
3998 let last_activity = self
3999 .messages
4000 .values()
4001 .filter(|m| m.channel_id == ch.id)
4002 .map(|m| m.timestamp.timestamp() as u64)
4003 .max()
4004 .unwrap_or(0);
4005 ConversationSummary {
4006 channel_id: ch.id,
4007 participants: ch.participants.clone(),
4008 message_count: msg_count,
4009 last_activity,
4010 }
4011 })
4012 .collect();
4013 summaries.truncate(limit as usize);
4014 summaries
4015 }
4016
4017 pub fn get_federation_status(&self) -> serde_json::Value {
4023 serde_json::json!({
4024 "enabled": self.federation_config.enabled,
4025 "local_zone": self.federation_config.local_zone,
4026 "zone_count": self.federation_config.zones.len(),
4027 "zones": self.federation_config.zones.iter().map(|z| &z.zone_id).collect::<Vec<_>>(),
4028 "default_policy": format!("{}", self.federation_config.default_policy),
4029 })
4030 }
4031
4032 pub fn set_federation_policy(
4034 &mut self,
4035 zone_id: &str,
4036 allow_semantic: bool,
4037 allow_affect: bool,
4038 allow_hive: bool,
4039 max_message_size: u64,
4040 ) -> ZonePolicyConfig {
4041 let config = ZonePolicyConfig {
4042 zone_id: zone_id.to_string(),
4043 allow_semantic,
4044 allow_affect,
4045 allow_hive,
4046 max_message_size,
4047 };
4048 self.zone_policies.insert(zone_id.to_string(), config.clone());
4049 config
4050 }
4051
4052 pub fn ground_claim(&self, claim: &str) -> GroundingResult {
4058 let claim_lower = claim.to_lowercase();
4059 let mut evidence = Vec::new();
4060 let mut score = 0.0f64;
4061
4062 for ch in self.channels.values() {
4064 if claim_lower.contains(&ch.name.to_lowercase()) {
4065 evidence.push(GroundingEvidence {
4066 evidence_type: "channel".to_string(),
4067 source: String::new(),
4068 timestamp: 0,
4069 content: format!("Channel '{}' (id={}, state={})", ch.name, ch.id, ch.state),
4070 relevance: 0.9,
4071 });
4072 score = score.max(0.8);
4073 }
4074 for p in &ch.participants {
4075 if claim_lower.contains(&p.to_lowercase()) {
4076 evidence.push(GroundingEvidence {
4077 evidence_type: "participant".to_string(),
4078 source: String::new(),
4079 timestamp: 0,
4080 content: format!("'{}' is a participant in channel '{}'", p, ch.name),
4081 relevance: 0.8,
4082 });
4083 score = score.max(0.7);
4084 }
4085 }
4086 }
4087
4088 for msg in self.messages.values() {
4090 if claim_lower.contains(&msg.sender.to_lowercase())
4091 || claim_lower.contains(&msg.content.to_lowercase().chars().take(50).collect::<String>())
4092 {
4093 evidence.push(GroundingEvidence {
4094 evidence_type: "message".to_string(),
4095 source: String::new(),
4096 timestamp: 0,
4097 content: format!(
4098 "Message from '{}' in channel {} at {}",
4099 msg.sender, msg.channel_id, msg.timestamp
4100 ),
4101 relevance: 0.7,
4102 });
4103 score = score.max(0.6);
4104 }
4105 }
4106
4107 for gate in &self.consent_gates {
4109 if claim_lower.contains(&gate.grantor.to_lowercase())
4110 || claim_lower.contains(&gate.grantee.to_lowercase())
4111 {
4112 evidence.push(GroundingEvidence {
4113 evidence_type: "consent".to_string(),
4114 source: String::new(),
4115 timestamp: 0,
4116 content: format!(
4117 "Consent: {} -> {} ({}, status={})",
4118 gate.grantor, gate.grantee, gate.scope, gate.status
4119 ),
4120 relevance: 0.8,
4121 });
4122 score = score.max(0.7);
4123 }
4124 }
4125
4126 for (agent, level) in &self.trust_levels {
4128 if claim_lower.contains(&agent.to_lowercase()) {
4129 evidence.push(GroundingEvidence {
4130 evidence_type: "trust".to_string(),
4131 source: String::new(),
4132 timestamp: 0,
4133 content: format!("Trust level for '{}': {}", agent, level),
4134 relevance: 0.8,
4135 });
4136 score = score.max(0.7);
4137 }
4138 }
4139
4140 for hive in self.hive_minds.values() {
4142 if claim_lower.contains(&hive.name.to_lowercase()) {
4143 evidence.push(GroundingEvidence {
4144 evidence_type: "hive".to_string(),
4145 source: String::new(),
4146 timestamp: 0,
4147 content: format!(
4148 "Hive '{}' (id={}, members={})",
4149 hive.name, hive.id, hive.constituents.len()
4150 ),
4151 relevance: 0.8,
4152 });
4153 score = score.max(0.7);
4154 }
4155 }
4156
4157 let status = if score >= 0.7 {
4158 GroundingStatus::Verified
4159 } else if score >= 0.3 {
4160 GroundingStatus::Partial
4161 } else {
4162 GroundingStatus::Ungrounded
4163 };
4164
4165 GroundingResult {
4166 claim: claim.to_string(),
4167 status,
4168 evidence,
4169 confidence: score,
4170 }
4171 }
4172
4173 pub fn generate_key(
4183 &mut self,
4184 algorithm: &str,
4185 channel_id: Option<u64>,
4186 ) -> CommResult<KeyEntry> {
4187 let id = self.next_key_id;
4188 self.next_key_id += 1;
4189
4190 let now = std::time::SystemTime::now()
4192 .duration_since(std::time::UNIX_EPOCH)
4193 .unwrap_or_default()
4194 .as_secs();
4195 let fingerprint = format!("{:016x}", now.wrapping_mul(6364136223846793005).wrapping_add(id));
4196
4197 let entry = KeyEntry {
4198 id,
4199 algorithm: algorithm.to_string(),
4200 created_at: now,
4201 status: "active".to_string(),
4202 channel_id,
4203 fingerprint,
4204 };
4205
4206 self.key_store.push(entry.clone());
4207 Ok(entry)
4208 }
4209
4210 pub fn list_keys(&self) -> Vec<&KeyEntry> {
4212 self.key_store.iter().collect()
4213 }
4214
4215 pub fn get_key(&self, key_id: u64) -> CommResult<&KeyEntry> {
4217 self.key_store
4218 .iter()
4219 .find(|k| k.id == key_id)
4220 .ok_or(CommError::KeyNotFound(key_id))
4221 }
4222
4223 pub fn rotate_key(&mut self, key_id: u64) -> CommResult<KeyEntry> {
4226 let (algorithm, channel_id) = {
4228 let old_key = self
4229 .key_store
4230 .iter_mut()
4231 .find(|k| k.id == key_id)
4232 .ok_or(CommError::KeyNotFound(key_id))?;
4233
4234 if old_key.status == "revoked" {
4235 return Err(CommError::KeyNotFound(key_id));
4236 }
4237
4238 let alg = old_key.algorithm.clone();
4239 let ch = old_key.channel_id;
4240 old_key.status = "rotated".to_string();
4241 (alg, ch)
4242 };
4243
4244 self.generate_key(&algorithm, channel_id)
4246 }
4247
4248 pub fn revoke_key(&mut self, key_id: u64) -> CommResult<()> {
4250 let key = self
4251 .key_store
4252 .iter_mut()
4253 .find(|k| k.id == key_id)
4254 .ok_or(CommError::KeyNotFound(key_id))?;
4255
4256 key.status = "revoked".to_string();
4257 Ok(())
4258 }
4259
4260 pub fn export_key(&self, key_id: u64) -> CommResult<String> {
4262 let key = self.get_key(key_id)?;
4263 Ok(key.fingerprint.clone())
4264 }
4265
4266 pub fn ground_evidence(&self, query: &str) -> Vec<GroundingEvidence> {
4274 let query_lower = query.to_lowercase();
4275 let mut evidence = Vec::new();
4276
4277 for msg in self.messages.values() {
4279 let content_lower = msg.content.to_lowercase();
4280 let sender_lower = msg.sender.to_lowercase();
4281 if content_lower.contains(&query_lower) || sender_lower.contains(&query_lower) {
4282 let relevance = if content_lower.contains(&query_lower) && sender_lower.contains(&query_lower) {
4283 1.0
4284 } else if content_lower.contains(&query_lower) {
4285 0.8
4286 } else {
4287 0.6
4288 };
4289 evidence.push(GroundingEvidence {
4290 evidence_type: format!("message(id={}, ts={})", msg.id, msg.timestamp.timestamp()),
4291 source: "messages".to_string(),
4292 timestamp: msg.timestamp.timestamp() as u64,
4293 content: format!(
4294 "[{}] {}: {}",
4295 msg.timestamp.to_rfc3339(),
4296 msg.sender,
4297 msg.content.chars().take(200).collect::<String>()
4298 ),
4299 relevance,
4300 });
4301 }
4302 }
4303
4304 for ch in self.channels.values() {
4306 if ch.name.to_lowercase().contains(&query_lower) {
4307 evidence.push(GroundingEvidence {
4308 evidence_type: format!("channel(id={}, created={})", ch.id, ch.created_at.timestamp()),
4309 source: "channels".to_string(),
4310 timestamp: ch.created_at.timestamp() as u64,
4311 content: format!(
4312 "Channel '{}' (type={}, state={}, participants={})",
4313 ch.name, ch.channel_type, ch.state, ch.participants.len()
4314 ),
4315 relevance: 0.9,
4316 });
4317 }
4318 for p in &ch.participants {
4320 if p.to_lowercase().contains(&query_lower) {
4321 evidence.push(GroundingEvidence {
4322 evidence_type: format!("agent(channel={})", ch.name),
4323 source: "agents".to_string(),
4324 timestamp: 0,
4325 content: format!("Agent '{}' in channel '{}'", p, ch.name),
4326 relevance: 0.7,
4327 });
4328 }
4329 }
4330 }
4331
4332 for hive in self.hive_minds.values() {
4334 if hive.name.to_lowercase().contains(&query_lower) {
4335 evidence.push(GroundingEvidence {
4336 evidence_type: format!("hive(id={})", hive.id),
4337 source: "hives".to_string(),
4338 timestamp: 0,
4339 content: format!(
4340 "Hive '{}' with {} constituents",
4341 hive.name,
4342 hive.constituents.len()
4343 ),
4344 relevance: 0.8,
4345 });
4346 }
4347 }
4348
4349 evidence.sort_by(|a, b| b.relevance.partial_cmp(&a.relevance).unwrap_or(std::cmp::Ordering::Equal));
4351 evidence
4352 }
4353
4354 pub fn ground_suggest(&self, query: &str, limit: usize) -> Vec<String> {
4357 let query_lower = query.to_lowercase();
4358 let mut suggestions = Vec::new();
4359
4360 for ch in self.channels.values() {
4362 if ch.name.to_lowercase().contains(&query_lower) {
4363 suggestions.push(format!("channel:{}", ch.name));
4364 }
4365 }
4366
4367 let mut seen_agents = std::collections::HashSet::new();
4369 for ch in self.channels.values() {
4370 for p in &ch.participants {
4371 if p.to_lowercase().contains(&query_lower) && seen_agents.insert(p.clone()) {
4372 suggestions.push(format!("agent:{}", p));
4373 }
4374 }
4375 }
4376
4377 for agent in self.trust_levels.keys() {
4379 if agent.to_lowercase().contains(&query_lower) && seen_agents.insert(agent.clone()) {
4380 suggestions.push(format!("agent:{}", agent));
4381 }
4382 }
4383
4384 for hive in self.hive_minds.values() {
4386 if hive.name.to_lowercase().contains(&query_lower) {
4387 suggestions.push(format!("hive:{}", hive.name));
4388 }
4389 }
4390
4391 let mut content_seen = std::collections::HashSet::new();
4393 for msg in self.messages.values() {
4394 if msg.content.to_lowercase().contains(&query_lower) {
4395 let snippet: String = msg.content.chars().take(80).collect();
4396 if content_seen.insert(snippet.clone()) {
4397 suggestions.push(format!("message:{}", snippet));
4398 }
4399 }
4400 }
4401
4402 suggestions.truncate(limit);
4403 suggestions
4404 }
4405
4406 pub fn assign_comm_ids(&mut self) {
4415 let msg_ids: Vec<u64> = self.messages.keys().copied().collect();
4416 for id in msg_ids {
4417 if let Some(msg) = self.messages.get_mut(&id) {
4418 if msg.comm_id.is_none() {
4419 msg.comm_id = Some(CommId::from_u64(msg.id));
4420 }
4421 }
4422 }
4423 let chan_ids: Vec<u64> = self.channels.keys().copied().collect();
4424 for id in chan_ids {
4425 if let Some(channel) = self.channels.get_mut(&id) {
4426 if channel.comm_id.is_none() {
4427 channel.comm_id = Some(CommId::from_u64(channel.id));
4428 }
4429 }
4430 }
4431 }
4432
4433 pub fn get_message_by_comm_id(&self, comm_id: &CommId) -> Option<&Message> {
4435 self.messages.values().find(|m| m.comm_id.as_ref() == Some(comm_id))
4436 }
4437
4438 pub fn get_channel_by_comm_id(&self, comm_id: &CommId) -> Option<&Channel> {
4440 self.channels.values().find(|c| c.comm_id.as_ref() == Some(comm_id))
4441 }
4442
4443 pub fn send_rich_message(
4448 &mut self,
4449 channel_id: u64,
4450 sender: &str,
4451 content: MessageContent,
4452 msg_type: MessageType,
4453 ) -> CommResult<Message> {
4454 let text = content.as_text().to_string();
4455 let rich_json = serde_json::to_string(&content)
4456 .map_err(|e| CommError::InvalidContent(format!("Failed to serialize rich content: {}", e)))?;
4457 let mut msg = self.send_message(channel_id, sender, &text, msg_type)?;
4458 if let Some(stored) = self.messages.get_mut(&msg.id) {
4460 stored.rich_content_json = Some(rich_json.clone());
4461 msg.rich_content_json = Some(rich_json);
4462 }
4463 Ok(msg)
4464 }
4465
4466 pub fn get_rich_content(&self, message_id: u64) -> CommResult<Option<MessageContent>> {
4468 let msg = self.messages.get(&message_id)
4469 .ok_or(CommError::MessageNotFound(message_id))?;
4470 match &msg.rich_content_json {
4471 Some(json_str) => {
4472 let content: MessageContent = serde_json::from_str(json_str)
4473 .map_err(|e| CommError::InvalidContent(format!("Failed to parse rich content: {}", e)))?;
4474 Ok(Some(content))
4475 }
4476 None => Ok(None),
4477 }
4478 }
4479
4480 pub fn store_embedding(&mut self, message_id: u64, embedding: Vec<f32>) {
4491 self.embeddings.insert(message_id, embedding);
4492 }
4493
4494 pub fn semantic_search(
4501 &self,
4502 query_embedding: &[f32],
4503 top_k: usize,
4504 ) -> Vec<(u64, f32)> {
4505 let mut scored: Vec<(u64, f32)> = self
4506 .embeddings
4507 .iter()
4508 .map(|(&msg_id, emb)| (msg_id, Self::cosine_similarity(query_embedding, emb)))
4509 .collect();
4510
4511 scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
4513 scored.truncate(top_k);
4514 scored
4515 }
4516
4517 pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
4522 let len = a.len().min(b.len());
4523 let mut dot = 0.0_f32;
4524 let mut norm_a = 0.0_f32;
4525 let mut norm_b = 0.0_f32;
4526
4527 for i in 0..len {
4528 dot += a[i] * b[i];
4529 norm_a += a[i] * a[i];
4530 norm_b += b[i] * b[i];
4531 }
4532
4533 let denom = norm_a.sqrt() * norm_b.sqrt();
4534 if denom == 0.0 {
4535 0.0
4536 } else {
4537 dot / denom
4538 }
4539 }
4540}
4541
4542#[derive(Debug, Clone, Serialize, Deserialize)]
4544pub struct CommStoreStats {
4545 pub channel_count: usize,
4547 pub message_count: usize,
4549 pub subscription_count: usize,
4551 pub total_participants: usize,
4553 #[serde(default)]
4555 pub dead_letter_count: usize,
4556 #[serde(default)]
4558 pub messages_by_type: HashMap<String, usize>,
4559 #[serde(default)]
4561 pub messages_by_priority: HashMap<String, usize>,
4562 #[serde(default)]
4564 pub channels_by_state: HashMap<String, usize>,
4565 #[serde(default)]
4567 pub oldest_message: Option<DateTime<Utc>>,
4568 #[serde(default)]
4570 pub newest_message: Option<DateTime<Utc>>,
4571 #[serde(default)]
4573 pub consent_gate_count: usize,
4574 #[serde(default)]
4576 pub trust_override_count: usize,
4577 #[serde(default)]
4579 pub temporal_queue_count: usize,
4580 #[serde(default)]
4582 pub hive_count: usize,
4583 #[serde(default)]
4585 pub comm_log_count: usize,
4586 #[serde(default)]
4588 pub federation_enabled: bool,
4589 #[serde(default)]
4591 pub federated_zone_count: usize,
4592 #[serde(default)]
4594 pub audit_log_count: usize,
4595}
4596
4597#[derive(Debug, Clone)]
4607pub struct MessageEngine {
4608 pub store: CommStore,
4610}
4611
4612impl Default for MessageEngine {
4613 fn default() -> Self {
4614 Self::new()
4615 }
4616}
4617
4618impl MessageEngine {
4619 pub fn new() -> Self {
4621 Self {
4622 store: CommStore::new(),
4623 }
4624 }
4625
4626 pub fn from_store(store: CommStore) -> Self {
4628 Self { store }
4629 }
4630
4631 pub fn process_message(
4640 &mut self,
4641 channel_id: u64,
4642 sender: &str,
4643 content: &str,
4644 message_type: &str,
4645 ) -> Result<u64, String> {
4646 let msg_type: MessageType = message_type
4648 .parse()
4649 .map_err(|e: String| format!("Invalid message type: {e}"))?;
4650
4651 let trust = self.store.get_trust_level(sender);
4653 if trust < CommTrustLevel::Basic {
4654 return Err(format!(
4655 "Sender '{}' has insufficient trust level: {} (need at least basic)",
4656 sender, trust
4657 ));
4658 }
4659
4660 if !self.store.check_consent_for_action(sender, "", ConsentScope::SendMessages) {
4663 return Err(format!(
4664 "Consent denied: sender '{}' lacks SendMessages consent",
4665 sender
4666 ));
4667 }
4668
4669 let message = self
4671 .store
4672 .send_message(channel_id, sender, content, msg_type)
4673 .map_err(|e| e.to_string())?;
4674
4675 Ok(message.id)
4677 }
4678
4679 pub fn query(
4695 &self,
4696 query_type: &str,
4697 params: &serde_json::Value,
4698 ) -> Result<serde_json::Value, String> {
4699 match query_type {
4700 "channel_messages" => {
4701 let channel_id = params
4702 .get("channel_id")
4703 .and_then(|v| v.as_u64())
4704 .ok_or_else(|| "Missing or invalid 'channel_id' parameter".to_string())?;
4705
4706 let msgs: Vec<&Message> = self
4707 .store
4708 .messages
4709 .values()
4710 .filter(|m| m.channel_id == channel_id)
4711 .collect();
4712
4713 serde_json::to_value(&msgs).map_err(|e| e.to_string())
4714 }
4715
4716 "message" => {
4717 let message_id = params
4718 .get("message_id")
4719 .and_then(|v| v.as_u64())
4720 .ok_or_else(|| "Missing or invalid 'message_id' parameter".to_string())?;
4721
4722 let msg = self
4723 .store
4724 .messages
4725 .get(&message_id)
4726 .ok_or_else(|| format!("Message {} not found", message_id))?;
4727
4728 serde_json::to_value(msg).map_err(|e| e.to_string())
4729 }
4730
4731 "stats" => {
4732 let stats = self.store.stats();
4733 serde_json::to_value(&stats).map_err(|e| e.to_string())
4734 }
4735
4736 "semantic_search" => {
4737 let embedding_val = params
4738 .get("embedding")
4739 .and_then(|v| v.as_array())
4740 .ok_or_else(|| "Missing or invalid 'embedding' parameter".to_string())?;
4741
4742 let embedding: Vec<f32> = embedding_val
4743 .iter()
4744 .filter_map(|v| v.as_f64().map(|f| f as f32))
4745 .collect();
4746
4747 let top_k = params
4748 .get("top_k")
4749 .and_then(|v| v.as_u64())
4750 .unwrap_or(10) as usize;
4751
4752 let results = self.store.semantic_search(&embedding, top_k);
4753 serde_json::to_value(&results).map_err(|e| e.to_string())
4754 }
4755
4756 other => Err(format!("Unknown query type: '{}'", other)),
4757 }
4758 }
4759}
4760
4761#[cfg(test)]
4766mod tests {
4767 use super::*;
4768 use filetime::FileTime;
4769
4770 fn new_store_with_channel() -> (CommStore, u64) {
4771 let mut store = CommStore::new();
4772 let ch = store
4773 .create_channel("test-channel", ChannelType::Group, None)
4774 .unwrap();
4775 (store, ch.id)
4776 }
4777
4778 #[test]
4781 fn test_create_channel() {
4782 let mut store = CommStore::new();
4783 let ch = store
4784 .create_channel("my-channel", ChannelType::Group, None)
4785 .unwrap();
4786 assert_eq!(ch.name, "my-channel");
4787 assert_eq!(ch.channel_type, ChannelType::Group);
4788 assert!(ch.participants.is_empty());
4789 }
4790
4791 #[test]
4792 fn test_create_channel_invalid_name_empty() {
4793 let mut store = CommStore::new();
4794 let result = store.create_channel("", ChannelType::Group, None);
4795 assert!(result.is_err());
4796 }
4797
4798 #[test]
4799 fn test_create_channel_invalid_name_special_chars() {
4800 let mut store = CommStore::new();
4801 let result = store.create_channel("bad channel!", ChannelType::Group, None);
4802 assert!(result.is_err());
4803 }
4804
4805 #[test]
4806 fn test_create_channel_long_name() {
4807 let mut store = CommStore::new();
4808 let long_name = "a".repeat(129);
4809 let result = store.create_channel(&long_name, ChannelType::Group, None);
4810 assert!(result.is_err());
4811 }
4812
4813 #[test]
4814 fn test_list_channels() {
4815 let mut store = CommStore::new();
4816 store
4817 .create_channel("alpha", ChannelType::Direct, None)
4818 .unwrap();
4819 store
4820 .create_channel("beta", ChannelType::Group, None)
4821 .unwrap();
4822 let channels = store.list_channels();
4823 assert_eq!(channels.len(), 2);
4824 assert_eq!(channels[0].name, "alpha");
4825 assert_eq!(channels[1].name, "beta");
4826 }
4827
4828 #[test]
4829 fn test_get_channel() {
4830 let mut store = CommStore::new();
4831 let ch = store
4832 .create_channel("find-me", ChannelType::Broadcast, None)
4833 .unwrap();
4834 assert!(store.get_channel(ch.id).is_some());
4835 assert!(store.get_channel(999).is_none());
4836 }
4837
4838 #[test]
4839 fn test_join_channel() {
4840 let (mut store, cid) = new_store_with_channel();
4841 store.join_channel(cid, "alice").unwrap();
4842 let ch = store.get_channel(cid).unwrap();
4843 assert_eq!(ch.participants, vec!["alice"]);
4844 }
4845
4846 #[test]
4847 fn test_join_channel_duplicate() {
4848 let (mut store, cid) = new_store_with_channel();
4849 store.join_channel(cid, "alice").unwrap();
4850 let result = store.join_channel(cid, "alice");
4851 assert!(result.is_err());
4852 }
4853
4854 #[test]
4855 fn test_join_channel_full() {
4856 let mut store = CommStore::new();
4857 let config = ChannelConfig {
4858 max_participants: 1,
4859 ..Default::default()
4860 };
4861 let ch = store
4862 .create_channel("tiny", ChannelType::Group, Some(config))
4863 .unwrap();
4864 store.join_channel(ch.id, "alice").unwrap();
4865 let result = store.join_channel(ch.id, "bob");
4866 assert!(result.is_err());
4867 }
4868
4869 #[test]
4870 fn test_leave_channel() {
4871 let (mut store, cid) = new_store_with_channel();
4872 store.join_channel(cid, "alice").unwrap();
4873 store.leave_channel(cid, "alice").unwrap();
4874 let ch = store.get_channel(cid).unwrap();
4875 assert!(ch.participants.is_empty());
4876 }
4877
4878 #[test]
4879 fn test_leave_channel_not_member() {
4880 let (mut store, cid) = new_store_with_channel();
4881 let result = store.leave_channel(cid, "ghost");
4882 assert!(result.is_err());
4883 }
4884
4885 #[test]
4888 fn test_send_message() {
4889 let (mut store, cid) = new_store_with_channel();
4890 let msg = store
4891 .send_message(cid, "alice", "hello world", MessageType::Text)
4892 .unwrap();
4893 assert_eq!(msg.sender, "alice");
4894 assert_eq!(msg.content, "hello world");
4895 assert_eq!(msg.message_type, MessageType::Text);
4896 assert!(msg.signature.is_some());
4897 }
4898
4899 #[test]
4900 fn test_send_message_empty_content() {
4901 let (mut store, cid) = new_store_with_channel();
4902 let result = store.send_message(cid, "alice", "", MessageType::Text);
4903 assert!(result.is_err());
4904 }
4905
4906 #[test]
4907 fn test_send_message_empty_sender() {
4908 let (mut store, cid) = new_store_with_channel();
4909 let result = store.send_message(cid, "", "hi", MessageType::Text);
4910 assert!(result.is_err());
4911 }
4912
4913 #[test]
4914 fn test_send_message_nonexistent_channel() {
4915 let mut store = CommStore::new();
4916 let result = store.send_message(999, "alice", "hi", MessageType::Text);
4917 assert!(result.is_err());
4918 }
4919
4920 #[test]
4921 fn test_receive_messages() {
4922 let (mut store, cid) = new_store_with_channel();
4923 store
4924 .send_message(cid, "alice", "msg1", MessageType::Text)
4925 .unwrap();
4926 store
4927 .send_message(cid, "bob", "msg2", MessageType::Text)
4928 .unwrap();
4929 let msgs = store.receive_messages(cid, None, None).unwrap();
4930 assert_eq!(msgs.len(), 2);
4931 }
4932
4933 #[test]
4934 fn test_receive_messages_with_since() {
4935 let (mut store, cid) = new_store_with_channel();
4936 store
4937 .send_message(cid, "alice", "old msg", MessageType::Text)
4938 .unwrap();
4939 let cutoff = Utc::now();
4940 store
4941 .send_message(cid, "alice", "new msg", MessageType::Text)
4942 .unwrap();
4943 let msgs = store.receive_messages(cid, None, Some(cutoff)).unwrap();
4944 assert_eq!(msgs.len(), 1);
4945 assert_eq!(msgs[0].content, "new msg");
4946 }
4947
4948 #[test]
4949 fn test_acknowledge_message() {
4950 let (mut store, cid) = new_store_with_channel();
4951 let msg = store
4952 .send_message(cid, "alice", "ack me", MessageType::Text)
4953 .unwrap();
4954 store.acknowledge_message(msg.id, "bob").unwrap();
4955 let updated = store.get_message(msg.id).unwrap();
4956 assert!(updated.acknowledged_by.contains(&"bob".to_string()));
4957 }
4958
4959 #[test]
4960 fn test_acknowledge_nonexistent() {
4961 let mut store = CommStore::new();
4962 let result = store.acknowledge_message(999, "bob");
4963 assert!(result.is_err());
4964 }
4965
4966 #[test]
4967 fn test_broadcast() {
4968 let (mut store, cid) = new_store_with_channel();
4969 store.join_channel(cid, "alice").unwrap();
4970 store.join_channel(cid, "bob").unwrap();
4971 store.join_channel(cid, "carol").unwrap();
4972 let msgs = store.broadcast(cid, "alice", "hello everyone").unwrap();
4973 assert_eq!(msgs.len(), 2);
4975 }
4976
4977 #[test]
4980 fn test_subscribe() {
4981 let mut store = CommStore::new();
4982 let sub = store.subscribe("weather", "sensor-1").unwrap();
4983 assert_eq!(sub.topic, "weather");
4984 assert_eq!(sub.subscriber, "sensor-1");
4985 }
4986
4987 #[test]
4988 fn test_unsubscribe() {
4989 let mut store = CommStore::new();
4990 let sub = store.subscribe("weather", "sensor-1").unwrap();
4991 store.unsubscribe(sub.id).unwrap();
4992 assert!(store.unsubscribe(sub.id).is_err());
4993 }
4994
4995 #[test]
4996 fn test_publish() {
4997 let mut store = CommStore::new();
4998 store.subscribe("alerts", "agent-a").unwrap();
4999 store.subscribe("alerts", "agent-b").unwrap();
5000 let msgs = store.publish("alerts", "monitor", "CPU high").unwrap();
5001 assert_eq!(msgs.len(), 2);
5002 }
5003
5004 #[test]
5007 fn test_search_messages() {
5008 let (mut store, cid) = new_store_with_channel();
5009 store
5010 .send_message(cid, "alice", "hello world", MessageType::Text)
5011 .unwrap();
5012 store
5013 .send_message(cid, "bob", "goodbye world", MessageType::Text)
5014 .unwrap();
5015 store
5016 .send_message(cid, "carol", "hello there", MessageType::Text)
5017 .unwrap();
5018 let results = store.search_messages("hello", 10);
5019 assert_eq!(results.len(), 2);
5020 }
5021
5022 #[test]
5023 fn test_query_history_with_filter() {
5024 let (mut store, cid) = new_store_with_channel();
5025 store
5026 .send_message(cid, "alice", "text msg", MessageType::Text)
5027 .unwrap();
5028 store
5029 .send_message(cid, "bob", "command msg", MessageType::Command)
5030 .unwrap();
5031 let filter = MessageFilter {
5032 message_type: Some(MessageType::Command),
5033 ..Default::default()
5034 };
5035 let results = store.query_history(cid, &filter);
5036 assert_eq!(results.len(), 1);
5037 assert_eq!(results[0].message_type, MessageType::Command);
5038 }
5039
5040 #[test]
5041 fn test_get_message() {
5042 let (mut store, cid) = new_store_with_channel();
5043 let msg = store
5044 .send_message(cid, "alice", "find me", MessageType::Text)
5045 .unwrap();
5046 assert!(store.get_message(msg.id).is_some());
5047 assert!(store.get_message(999).is_none());
5048 }
5049
5050 #[test]
5053 fn test_save_and_load() {
5054 let (mut store, cid) = new_store_with_channel();
5055 store.join_channel(cid, "alice").unwrap();
5056 store
5057 .send_message(cid, "alice", "persisted", MessageType::Text)
5058 .unwrap();
5059 store.subscribe("topic-a", "alice").unwrap();
5060
5061 let dir = tempfile::tempdir().unwrap();
5062 let path = dir.path().join("test.acomm");
5063 store.save(&path).unwrap();
5064
5065 let loaded = CommStore::load(&path).unwrap();
5066 assert_eq!(loaded.channels.len(), 1);
5067 assert_eq!(loaded.messages.len(), 1);
5068 assert_eq!(loaded.subscriptions.len(), 1);
5069 }
5070
5071 #[test]
5072 fn test_load_invalid_file() {
5073 let dir = tempfile::tempdir().unwrap();
5074 let path = dir.path().join("bad.acomm");
5075 std::fs::write(&path, b"not a valid file").unwrap();
5076 let result = CommStore::load(&path);
5077 assert!(result.is_err());
5078 }
5079
5080 #[test]
5083 fn test_stats() {
5084 let (mut store, cid) = new_store_with_channel();
5085 store.join_channel(cid, "alice").unwrap();
5086 store
5087 .send_message(cid, "alice", "hi", MessageType::Text)
5088 .unwrap();
5089 let stats = store.stats();
5090 assert_eq!(stats.channel_count, 1);
5091 assert_eq!(stats.message_count, 1);
5092 assert_eq!(stats.total_participants, 1);
5093 }
5094
5095 #[test]
5098 fn test_set_channel_config() {
5099 let (mut store, cid) = new_store_with_channel();
5100 let config = ChannelConfig {
5101 max_participants: 10,
5102 ttl_seconds: 3600,
5103 persistence: false,
5104 encryption_required: true,
5105 ..Default::default()
5106 };
5107 store.set_channel_config(cid, config).unwrap();
5108 let ch = store.get_channel(cid).unwrap();
5109 assert_eq!(ch.config.max_participants, 10);
5110 assert!(ch.config.encryption_required);
5111 }
5112
5113 #[test]
5116 fn test_message_type_roundtrip() {
5117 let types = vec![
5118 MessageType::Text,
5119 MessageType::Command,
5120 MessageType::Query,
5121 MessageType::Response,
5122 MessageType::Broadcast,
5123 MessageType::Notification,
5124 MessageType::Acknowledgment,
5125 MessageType::Error,
5126 ];
5127 for mt in types {
5128 let s = mt.to_string();
5129 let parsed: MessageType = s.parse().unwrap();
5130 assert_eq!(parsed, mt);
5131 }
5132 }
5133
5134 #[test]
5137 fn test_channel_type_roundtrip() {
5138 let types = vec![
5139 ChannelType::Direct,
5140 ChannelType::Group,
5141 ChannelType::Broadcast,
5142 ChannelType::PubSub,
5143 ];
5144 for ct in types {
5145 let s = ct.to_string();
5146 let parsed: ChannelType = s.parse().unwrap();
5147 assert_eq!(parsed, ct);
5148 }
5149 }
5150
5151 #[test]
5158 fn test_message_priority_ordering() {
5159 let mut priorities = vec![
5161 MessagePriority::Critical,
5162 MessagePriority::Low,
5163 MessagePriority::Urgent,
5164 MessagePriority::Normal,
5165 MessagePriority::High,
5166 ];
5167 priorities.sort();
5168 assert_eq!(
5169 priorities,
5170 vec![
5171 MessagePriority::Low,
5172 MessagePriority::Normal,
5173 MessagePriority::High,
5174 MessagePriority::Urgent,
5175 MessagePriority::Critical,
5176 ]
5177 );
5178 }
5179
5180 #[test]
5183 fn test_channel_state_pause_blocks_send() {
5184 let (mut store, cid) = new_store_with_channel();
5185 store.pause_channel(cid).unwrap();
5186 let result = store.send_message(cid, "alice", "blocked", MessageType::Text);
5187 assert!(result.is_err());
5188 assert_eq!(store.dead_letter_count(), 1);
5190 }
5191
5192 #[test]
5195 fn test_channel_state_drain_allows_receive() {
5196 let (mut store, cid) = new_store_with_channel();
5197 store
5199 .send_message(cid, "alice", "before drain", MessageType::Text)
5200 .unwrap();
5201 store.drain_channel(cid).unwrap();
5203 let msgs = store.receive_messages(cid, None, None).unwrap();
5205 assert_eq!(msgs.len(), 1);
5206 let result = store.send_message(cid, "bob", "blocked", MessageType::Text);
5208 assert!(result.is_err());
5209 }
5210
5211 #[test]
5214 fn test_channel_state_close_blocks_all() {
5215 let (mut store, cid) = new_store_with_channel();
5216 store
5217 .send_message(cid, "alice", "before close", MessageType::Text)
5218 .unwrap();
5219 store.close_channel(cid).unwrap();
5220 let send_result = store.send_message(cid, "bob", "nope", MessageType::Text);
5222 assert!(send_result.is_err());
5223 let recv_result = store.receive_messages(cid, None, None);
5225 assert!(recv_result.is_err());
5226 }
5227
5228 #[test]
5231 fn test_channel_resume_after_pause() {
5232 let (mut store, cid) = new_store_with_channel();
5233 store.pause_channel(cid).unwrap();
5234 let ch = store.get_channel(cid).unwrap();
5235 assert_eq!(ch.state, ChannelState::Paused);
5236
5237 store.resume_channel(cid).unwrap();
5238 let ch = store.get_channel(cid).unwrap();
5239 assert_eq!(ch.state, ChannelState::Active);
5240
5241 let msg = store
5243 .send_message(cid, "alice", "resumed", MessageType::Text)
5244 .unwrap();
5245 assert_eq!(msg.content, "resumed");
5246 }
5247
5248 #[test]
5251 fn test_send_reply() {
5252 let (mut store, cid) = new_store_with_channel();
5253 let parent = store
5254 .send_message(cid, "alice", "original question", MessageType::Query)
5255 .unwrap();
5256 let reply = store
5257 .send_reply(cid, parent.id, "bob", "answer here", MessageType::Response)
5258 .unwrap();
5259 assert_eq!(reply.reply_to, Some(parent.id));
5260 assert!(reply.thread_id.is_some());
5261 let updated_parent = store.get_message(parent.id).unwrap();
5263 assert!(updated_parent.thread_id.is_some());
5264 assert_eq!(updated_parent.thread_id, reply.thread_id);
5265 }
5266
5267 #[test]
5270 fn test_get_thread() {
5271 let (mut store, cid) = new_store_with_channel();
5272 let parent = store
5273 .send_message(cid, "alice", "start thread", MessageType::Text)
5274 .unwrap();
5275 let r1 = store
5276 .send_reply(cid, parent.id, "bob", "reply 1", MessageType::Response)
5277 .unwrap();
5278 let thread_id = r1.thread_id.clone().unwrap();
5279 store
5280 .send_reply(cid, parent.id, "carol", "reply 2", MessageType::Response)
5281 .unwrap();
5282
5283 let thread = store.get_thread(&thread_id);
5284 assert_eq!(thread.len(), 3);
5286 assert!(thread[0].timestamp <= thread[1].timestamp);
5288 assert!(thread[1].timestamp <= thread[2].timestamp);
5289 }
5290
5291 #[test]
5294 fn test_get_replies() {
5295 let (mut store, cid) = new_store_with_channel();
5296 let parent = store
5297 .send_message(cid, "alice", "parent msg", MessageType::Text)
5298 .unwrap();
5299 store
5300 .send_reply(cid, parent.id, "bob", "reply A", MessageType::Response)
5301 .unwrap();
5302 store
5303 .send_reply(cid, parent.id, "carol", "reply B", MessageType::Response)
5304 .unwrap();
5305 store
5307 .send_message(cid, "dave", "unrelated", MessageType::Text)
5308 .unwrap();
5309
5310 let replies = store.get_replies(parent.id);
5311 assert_eq!(replies.len(), 2);
5312 assert!(replies.iter().all(|r| r.reply_to == Some(parent.id)));
5313 }
5314
5315 #[test]
5318 fn test_dead_letter_on_closed_channel() {
5319 let (mut store, cid) = new_store_with_channel();
5320 store.close_channel(cid).unwrap();
5321
5322 let result = store.send_message(cid, "alice", "dropped", MessageType::Text);
5323 assert!(result.is_err());
5324 assert_eq!(store.dead_letter_count(), 1);
5325
5326 let dls = store.list_dead_letters();
5327 assert_eq!(dls.len(), 1);
5328 assert_eq!(dls[0].original_message.content, "dropped");
5329 assert_eq!(dls[0].reason, DeadLetterReason::ChannelClosed);
5330 }
5331
5332 #[test]
5335 fn test_dead_letter_replay() {
5336 let (mut store, cid) = new_store_with_channel();
5337 store.close_channel(cid).unwrap();
5338
5339 let _ = store.send_message(cid, "alice", "retry me", MessageType::Text);
5341 assert_eq!(store.dead_letter_count(), 1);
5342
5343 store.resume_channel(cid).unwrap();
5345
5346 let msg = store.replay_dead_letter(0).unwrap();
5348 assert_eq!(msg.content, "retry me");
5349 assert_eq!(msg.status, MessageStatus::Sent);
5350 assert_eq!(store.dead_letter_count(), 0);
5352 }
5353
5354 #[test]
5357 fn test_expire_messages() {
5358 let mut store = CommStore::new();
5359 let config = ChannelConfig {
5360 ttl_seconds: 1, ..Default::default()
5362 };
5363 let ch = store
5364 .create_channel("ephemeral", ChannelType::Group, Some(config))
5365 .unwrap();
5366
5367 let id = 100;
5369 let old_time = Utc::now() - chrono::Duration::seconds(10);
5370 let msg = Message {
5371 id,
5372 channel_id: ch.id,
5373 sender: "alice".to_string(),
5374 recipient: None,
5375 content: "old message".to_string(),
5376 message_type: MessageType::Text,
5377 timestamp: old_time,
5378 metadata: HashMap::new(),
5379 signature: None,
5380 acknowledged_by: Vec::new(),
5381 status: MessageStatus::Sent,
5382 priority: MessagePriority::Normal,
5383 reply_to: None,
5384 correlation_id: None,
5385 thread_id: None,
5386 comm_timestamp: CommTimestamp::default(),
5387 rich_content_json: None,
5388 comm_id: None,
5389 receipt_id: None,
5390 };
5391 store.messages.insert(id, msg);
5392
5393 store
5395 .send_message(ch.id, "bob", "fresh message", MessageType::Text)
5396 .unwrap();
5397
5398 let expired_count = store.expire_messages();
5399 assert_eq!(expired_count, 1);
5400 assert!(store.get_message(100).is_none());
5402 assert_eq!(store.dead_letter_count(), 1);
5404 let dls = store.list_dead_letters();
5405 assert_eq!(dls[0].reason, DeadLetterReason::Expired);
5406 assert_eq!(store.messages.len(), 1);
5408 }
5409
5410 #[test]
5413 fn test_compact_removes_closed_channel_messages() {
5414 let (mut store, cid) = new_store_with_channel();
5415 store
5416 .send_message(cid, "alice", "msg1", MessageType::Text)
5417 .unwrap();
5418 store
5419 .send_message(cid, "alice", "msg2", MessageType::Text)
5420 .unwrap();
5421
5422 let ch2 = store
5424 .create_channel("active-ch", ChannelType::Group, None)
5425 .unwrap();
5426 store
5427 .send_message(ch2.id, "bob", "active msg", MessageType::Text)
5428 .unwrap();
5429
5430 assert_eq!(store.messages.len(), 3);
5431
5432 store.close_channel(cid).unwrap();
5434
5435 let removed = store.compact();
5436 assert_eq!(removed, 2); assert_eq!(store.messages.len(), 1); }
5439
5440 #[test]
5443 fn test_delivery_mode_default() {
5444 let config = ChannelConfig::default();
5445 assert_eq!(config.delivery_mode, DeliveryMode::AtLeastOnce);
5446 assert_eq!(config.retention_policy, RetentionPolicy::Forever);
5447 }
5448
5449 #[test]
5452 fn test_enhanced_stats() {
5453 let (mut store, cid) = new_store_with_channel();
5454 store
5455 .send_message(cid, "alice", "text msg", MessageType::Text)
5456 .unwrap();
5457 store
5458 .send_message(cid, "bob", "command msg", MessageType::Command)
5459 .unwrap();
5460 store
5461 .send_message_with_priority(
5462 cid,
5463 "carol",
5464 "urgent msg",
5465 MessageType::Text,
5466 MessagePriority::Urgent,
5467 )
5468 .unwrap();
5469
5470 let ch2 = store
5472 .create_channel("closable", ChannelType::Group, None)
5473 .unwrap();
5474 store.close_channel(ch2.id).unwrap();
5475 let _ = store.send_message(ch2.id, "dave", "dropped", MessageType::Text);
5476
5477 let stats = store.stats();
5478 assert_eq!(stats.channel_count, 2);
5479 assert_eq!(stats.message_count, 3);
5480 assert_eq!(stats.dead_letter_count, 1);
5481
5482 assert_eq!(stats.messages_by_type.get("text"), Some(&2));
5484 assert_eq!(stats.messages_by_type.get("command"), Some(&1));
5485
5486 assert_eq!(stats.messages_by_priority.get("normal"), Some(&2));
5488 assert_eq!(stats.messages_by_priority.get("urgent"), Some(&1));
5489
5490 assert_eq!(stats.channels_by_state.get("active"), Some(&1));
5492 assert_eq!(stats.channels_by_state.get("closed"), Some(&1));
5493
5494 assert!(stats.oldest_message.is_some());
5496 assert!(stats.newest_message.is_some());
5497 }
5498
5499 #[test]
5502 fn test_message_status_transitions() {
5503 let (mut store, cid) = new_store_with_channel();
5504
5505 let msg = store
5507 .send_message(cid, "alice", "track me", MessageType::Text)
5508 .unwrap();
5509 assert_eq!(msg.status, MessageStatus::Sent);
5510
5511 store.acknowledge_message(msg.id, "bob").unwrap();
5513 let updated = store.get_message(msg.id).unwrap();
5514 assert_eq!(updated.status, MessageStatus::Acknowledged);
5515
5516 assert_eq!(MessageStatus::default(), MessageStatus::Created);
5518 }
5519
5520 #[test]
5523 fn test_send_message_with_priority() {
5524 let (mut store, cid) = new_store_with_channel();
5525 let msg = store
5526 .send_message_with_priority(
5527 cid,
5528 "alice",
5529 "critical alert",
5530 MessageType::Notification,
5531 MessagePriority::Critical,
5532 )
5533 .unwrap();
5534 assert_eq!(msg.priority, MessagePriority::Critical);
5535
5536 let stored = store.get_message(msg.id).unwrap();
5538 assert_eq!(stored.priority, MessagePriority::Critical);
5539 }
5540
5541 #[test]
5544 fn test_dead_letter_clear() {
5545 let (mut store, cid) = new_store_with_channel();
5546 store.close_channel(cid).unwrap();
5547 let _ = store.send_message(cid, "alice", "dl1", MessageType::Text);
5548 let _ = store.send_message(cid, "alice", "dl2", MessageType::Text);
5549 assert_eq!(store.dead_letter_count(), 2);
5550
5551 store.clear_dead_letters();
5552 assert_eq!(store.dead_letter_count(), 0);
5553 }
5554
5555 #[test]
5558 fn test_compact_enforces_retention_policy() {
5559 let mut store = CommStore::new();
5560 let config = ChannelConfig {
5561 retention_policy: RetentionPolicy::MessageCount(2),
5562 ..Default::default()
5563 };
5564 let ch = store
5565 .create_channel("limited", ChannelType::Group, Some(config))
5566 .unwrap();
5567
5568 for i in 0..5 {
5570 store
5571 .send_message(ch.id, "alice", &format!("msg-{i}"), MessageType::Text)
5572 .unwrap();
5573 }
5574 assert_eq!(store.messages.len(), 5);
5575
5576 let removed = store.compact();
5577 assert_eq!(removed, 3); assert_eq!(store.messages.len(), 2);
5579 }
5580
5581 #[test]
5584 fn test_save_and_load_with_new_fields() {
5585 let (mut store, cid) = new_store_with_channel();
5586 store.join_channel(cid, "alice").unwrap();
5587
5588 let msg = store
5590 .send_message_with_priority(
5591 cid,
5592 "alice",
5593 "priority msg",
5594 MessageType::Text,
5595 MessagePriority::High,
5596 )
5597 .unwrap();
5598 store
5599 .send_reply(cid, msg.id, "bob", "reply", MessageType::Response)
5600 .unwrap();
5601
5602 let ch2 = store
5604 .create_channel("closing", ChannelType::Group, None)
5605 .unwrap();
5606 store.close_channel(ch2.id).unwrap();
5607 let _ = store.send_message(ch2.id, "carol", "dead", MessageType::Text);
5608
5609 let dir = tempfile::tempdir().unwrap();
5610 let path = dir.path().join("test_new.acomm");
5611 store.save(&path).unwrap();
5612
5613 let loaded = CommStore::load(&path).unwrap();
5614 assert_eq!(loaded.channels.len(), 2);
5615 assert_eq!(loaded.messages.len(), 2);
5616 assert_eq!(loaded.dead_letters.len(), 1);
5617
5618 let loaded_msg = loaded.get_message(msg.id).unwrap();
5620 assert_eq!(loaded_msg.priority, MessagePriority::High);
5621
5622 let ch2_loaded = loaded.get_channel(ch2.id).unwrap();
5623 assert_eq!(ch2_loaded.state, ChannelState::Closed);
5624 }
5625
5626 #[test]
5629 fn test_channel_state_display() {
5630 assert_eq!(ChannelState::Active.to_string(), "active");
5631 assert_eq!(ChannelState::Paused.to_string(), "paused");
5632 assert_eq!(ChannelState::Draining.to_string(), "draining");
5633 assert_eq!(ChannelState::Closed.to_string(), "closed");
5634 }
5635
5636 #[test]
5639 fn test_dead_letter_on_nonexistent_channel() {
5640 let mut store = CommStore::new();
5641 let result = store.send_message(999, "alice", "nowhere", MessageType::Text);
5642 assert!(result.is_err());
5643 assert_eq!(store.dead_letter_count(), 1);
5644 let dls = store.list_dead_letters();
5645 assert_eq!(dls[0].reason, DeadLetterReason::ChannelNotFound);
5646 }
5647
5648 #[test]
5651 fn consent_grant_and_check() {
5652 let mut store = CommStore::new();
5653 store
5654 .grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None)
5655 .unwrap();
5656 assert!(store.check_consent("alice", "bob", &ConsentScope::ReadMessages));
5657 assert!(!store.check_consent("bob", "alice", &ConsentScope::ReadMessages));
5658 }
5659
5660 #[test]
5661 fn consent_revoke() {
5662 let mut store = CommStore::new();
5663 store
5664 .grant_consent("alice", "bob", ConsentScope::SendMessages, None, None)
5665 .unwrap();
5666 assert!(store.check_consent("alice", "bob", &ConsentScope::SendMessages));
5667 store
5668 .revoke_consent("alice", "bob", &ConsentScope::SendMessages)
5669 .unwrap();
5670 assert!(!store.check_consent("alice", "bob", &ConsentScope::SendMessages));
5671 }
5672
5673 #[test]
5674 fn consent_list_filtered() {
5675 let mut store = CommStore::new();
5676 store.grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None).unwrap();
5677 store.grant_consent("charlie", "bob", ConsentScope::SendMessages, None, None).unwrap();
5678 assert_eq!(store.list_consent_gates(Some("bob")).len(), 2);
5679 assert_eq!(store.list_consent_gates(Some("alice")).len(), 1);
5680 assert_eq!(store.list_consent_gates(None).len(), 2);
5681 }
5682
5683 #[test]
5686 fn trust_set_and_get() {
5687 let mut store = CommStore::new();
5688 assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::Standard);
5689 store.set_trust_level("agent-1", CommTrustLevel::High).unwrap();
5690 assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::High);
5691 }
5692
5693 #[test]
5694 fn trust_list() {
5695 let mut store = CommStore::new();
5696 store.set_trust_level("a", CommTrustLevel::Full).unwrap();
5697 store.set_trust_level("b", CommTrustLevel::Minimal).unwrap();
5698 assert_eq!(store.list_trust_levels().len(), 2);
5699 }
5700
5701 #[test]
5704 fn temporal_schedule_and_list() {
5705 let (mut store, ch_id) = new_store_with_channel();
5706 store.join_channel(ch_id, "alice").unwrap();
5707 store
5708 .schedule_message(ch_id, "alice", "hello future", TemporalTarget::Immediate, None)
5709 .unwrap();
5710 assert_eq!(store.list_scheduled().len(), 1);
5711 }
5712
5713 #[test]
5714 fn temporal_cancel() {
5715 let (mut store, ch_id) = new_store_with_channel();
5716 store.join_channel(ch_id, "alice").unwrap();
5717 let msg = store
5718 .schedule_message(ch_id, "alice", "later", TemporalTarget::FutureRelative { delay_seconds: 3600 }, None)
5719 .unwrap();
5720 let tid = msg.id;
5721 assert_eq!(store.list_scheduled().len(), 1);
5722 store.cancel_scheduled(tid).unwrap();
5723 assert_eq!(store.list_scheduled().len(), 0);
5724 }
5725
5726 #[test]
5727 fn temporal_deliver_immediate() {
5728 let (mut store, ch_id) = new_store_with_channel();
5729 store.join_channel(ch_id, "alice").unwrap();
5730 store
5731 .schedule_message(ch_id, "alice", "now!", TemporalTarget::Immediate, None)
5732 .unwrap();
5733 let delivered = store.deliver_pending_temporal();
5734 assert_eq!(delivered, 1);
5735 assert_eq!(store.list_scheduled().len(), 0);
5736 }
5737
5738 #[test]
5741 fn federation_configure() {
5742 let mut store = CommStore::new();
5743 store
5744 .configure_federation(true, "zone-a", FederationPolicy::Allow)
5745 .unwrap();
5746 let config = store.get_federation_config();
5747 assert!(config.enabled);
5748 assert_eq!(config.local_zone, "zone-a");
5749 }
5750
5751 #[test]
5752 fn federation_add_remove_zone() {
5753 let mut store = CommStore::new();
5754 store.add_federated_zone(FederatedZone {
5755 zone_id: "zone-b".to_string(),
5756 name: "Zone B".to_string(),
5757 endpoint: "https://b.example.com".to_string(),
5758 policy: FederationPolicy::Allow,
5759 trust_level: CommTrustLevel::High,
5760 }).unwrap();
5761 assert_eq!(store.list_federated_zones().len(), 1);
5762 store.remove_federated_zone("zone-b").unwrap();
5763 assert_eq!(store.list_federated_zones().len(), 0);
5764 }
5765
5766 #[test]
5767 fn federation_duplicate_zone_error() {
5768 let mut store = CommStore::new();
5769 let zone = FederatedZone {
5770 zone_id: "z1".to_string(),
5771 name: "Z1".to_string(),
5772 endpoint: String::new(),
5773 policy: FederationPolicy::Deny,
5774 trust_level: CommTrustLevel::Basic,
5775 };
5776 store.add_federated_zone(zone.clone()).unwrap();
5777 assert!(store.add_federated_zone(zone).is_err());
5778 }
5779
5780 #[test]
5783 fn hive_form_and_list() {
5784 let mut store = CommStore::new();
5785 store.form_hive("test-hive", "alice", CollectiveDecisionMode::Majority).unwrap();
5786 assert_eq!(store.list_hives().len(), 1);
5787 }
5788
5789 #[test]
5790 fn hive_join_and_leave() {
5791 let mut store = CommStore::new();
5792 let hive = store.form_hive("h1", "alice", CollectiveDecisionMode::Consensus).unwrap();
5793 let hid = hive.id;
5794 store.join_hive(hid, "bob", HiveRole::Member).unwrap();
5795 assert_eq!(store.get_hive(hid).unwrap().constituents.len(), 2);
5796 store.leave_hive(hid, "bob").unwrap();
5797 assert_eq!(store.get_hive(hid).unwrap().constituents.len(), 1);
5798 }
5799
5800 #[test]
5801 fn hive_dissolve() {
5802 let mut store = CommStore::new();
5803 let hive = store.form_hive("h2", "alice", CollectiveDecisionMode::CoordinatorDecides).unwrap();
5804 let hid = hive.id;
5805 store.dissolve_hive(hid).unwrap();
5806 assert!(store.get_hive(hid).is_none());
5807 }
5808
5809 #[test]
5810 fn hive_join_duplicate_error() {
5811 let mut store = CommStore::new();
5812 let hive = store.form_hive("h3", "alice", CollectiveDecisionMode::Unanimous).unwrap();
5813 let hid = hive.id;
5814 assert!(store.join_hive(hid, "alice", HiveRole::Member).is_err());
5815 }
5816
5817 #[test]
5820 fn comm_log_entries() {
5821 let mut store = CommStore::new();
5822 store.log_communication("hello", "user", Some("greeting".to_string()), None, None);
5823 store.log_communication("hi back", "agent", Some("greeting".to_string()), None, None);
5824 assert_eq!(store.get_comm_log(None).len(), 2);
5825 assert_eq!(store.get_comm_log(Some(1)).len(), 1);
5826 }
5827
5828 #[test]
5831 fn grounding_verified_channel() {
5832 let (store, _ch_id) = new_store_with_channel();
5833 let result = store.ground_claim("test-channel exists");
5834 assert_eq!(result.status, GroundingStatus::Verified);
5835 assert!(!result.evidence.is_empty());
5836 }
5837
5838 #[test]
5839 fn grounding_ungrounded() {
5840 let store = CommStore::new();
5841 let result = store.ground_claim("nonexistent-thing");
5842 assert_eq!(result.status, GroundingStatus::Ungrounded);
5843 assert!(result.evidence.is_empty());
5844 }
5845
5846 #[test]
5847 fn grounding_trust_evidence() {
5848 let mut store = CommStore::new();
5849 store.set_trust_level("agent-x", CommTrustLevel::High).unwrap();
5850 let result = store.ground_claim("agent-x has trust");
5851 assert_ne!(result.status, GroundingStatus::Ungrounded);
5852 }
5853
5854 #[test]
5857 fn stats_include_new_fields() {
5858 let mut store = CommStore::new();
5859 store.grant_consent("a", "b", ConsentScope::ReadMessages, None, None).unwrap();
5860 store.set_trust_level("x", CommTrustLevel::Full).unwrap();
5861 store.form_hive("h", "coord", CollectiveDecisionMode::Majority).unwrap();
5862 let stats = store.stats();
5863 assert_eq!(stats.consent_gate_count, 1);
5864 assert_eq!(stats.trust_override_count, 1);
5865 assert_eq!(stats.hive_count, 1);
5866 }
5867
5868 #[test]
5871 fn affect_message_send() {
5872 let (mut store, ch_id) = new_store_with_channel();
5873 store.join_channel(ch_id, "alice").unwrap();
5874 let msg = store
5875 .send_affect_message(
5876 ch_id,
5877 "alice",
5878 "I'm excited!",
5879 AffectState {
5880 valence: 0.8,
5881 arousal: 0.9,
5882 ..Default::default()
5883 },
5884 )
5885 .unwrap();
5886 assert!(msg.content.contains("[affect:"));
5887 assert!(msg.content.contains("I'm excited!"));
5888 }
5889
5890 #[test]
5898 fn consent_grant_with_reason_and_expiry() {
5899 let mut store = CommStore::new();
5900 let entry = store
5901 .grant_consent(
5902 "alice",
5903 "bob",
5904 ConsentScope::ScheduleMessages,
5905 Some("project collaboration".to_string()),
5906 Some("2030-12-31T23:59:59Z".to_string()),
5907 )
5908 .unwrap();
5909 assert_eq!(entry.grantor, "alice");
5910 assert_eq!(entry.grantee, "bob");
5911 assert_eq!(entry.scope, ConsentScope::ScheduleMessages);
5912 assert_eq!(entry.status, ConsentStatus::Granted);
5913 assert_eq!(entry.reason.as_deref(), Some("project collaboration"));
5914 assert_eq!(entry.expires_at.as_deref(), Some("2030-12-31T23:59:59Z"));
5915 }
5916
5917 #[test]
5918 fn consent_update_existing() {
5919 let mut store = CommStore::new();
5920 store
5922 .grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None)
5923 .unwrap();
5924 assert!(store.check_consent("alice", "bob", &ConsentScope::ReadMessages));
5925
5926 store
5928 .grant_consent(
5929 "alice",
5930 "bob",
5931 ConsentScope::ReadMessages,
5932 Some("updated reason".to_string()),
5933 Some("2031-01-01T00:00:00Z".to_string()),
5934 )
5935 .unwrap();
5936
5937 let gates = store.list_consent_gates(Some("alice"));
5939 let matching: Vec<_> = gates
5940 .iter()
5941 .filter(|e| {
5942 e.grantor == "alice"
5943 && e.grantee == "bob"
5944 && e.scope == ConsentScope::ReadMessages
5945 })
5946 .collect();
5947 assert_eq!(matching.len(), 1, "Should update existing, not create duplicate");
5948 assert_eq!(matching[0].reason.as_deref(), Some("updated reason"));
5949 }
5950
5951 #[test]
5952 fn consent_revoke_nonexistent_error() {
5953 let mut store = CommStore::new();
5954 let result = store.revoke_consent("alice", "bob", &ConsentScope::Federate);
5955 assert!(result.is_err(), "Revoking non-existent consent should fail");
5956 }
5957
5958 #[test]
5959 fn consent_empty_grantor_error() {
5960 let mut store = CommStore::new();
5961 let result = store.grant_consent("", "bob", ConsentScope::ReadMessages, None, None);
5962 assert!(result.is_err(), "Empty grantor should return error");
5963 }
5964
5965 #[test]
5966 fn consent_list_empty_returns_empty() {
5967 let store = CommStore::new();
5968 let gates = store.list_consent_gates(None);
5969 assert!(gates.is_empty(), "Fresh store should have no consent gates");
5970 let gates_filtered = store.list_consent_gates(Some("nobody"));
5971 assert!(gates_filtered.is_empty());
5972 }
5973
5974 #[test]
5977 fn trust_empty_agent_error() {
5978 let mut store = CommStore::new();
5979 let result = store.set_trust_level("", CommTrustLevel::High);
5980 assert!(result.is_err(), "Empty agent_id should return error");
5981 }
5982
5983 #[test]
5984 fn trust_override_existing() {
5985 let mut store = CommStore::new();
5986 store.set_trust_level("agent-1", CommTrustLevel::Basic).unwrap();
5987 assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::Basic);
5988 store.set_trust_level("agent-1", CommTrustLevel::Absolute).unwrap();
5989 assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::Absolute);
5990 assert_eq!(store.list_trust_levels().len(), 1);
5992 }
5993
5994 #[test]
5995 fn trust_default_is_standard() {
5996 let store = CommStore::new();
5997 assert_eq!(
5998 store.get_trust_level("unknown-agent"),
5999 CommTrustLevel::Standard,
6000 "Fresh agent should default to Standard trust"
6001 );
6002 }
6003
6004 #[test]
6007 fn temporal_schedule_to_nonexistent_channel() {
6008 let mut store = CommStore::new();
6009 let result = store.schedule_message(
6010 999,
6011 "alice",
6012 "hello",
6013 TemporalTarget::Immediate,
6014 None,
6015 );
6016 assert!(result.is_err(), "Scheduling to non-existent channel should fail");
6017 }
6018
6019 #[test]
6020 fn temporal_cancel_delivered_error() {
6021 let (mut store, ch_id) = new_store_with_channel();
6022 let msg = store
6023 .schedule_message(ch_id, "alice", "now", TemporalTarget::Immediate, None)
6024 .unwrap();
6025 let tid = msg.id;
6026
6027 let delivered = store.deliver_pending_temporal();
6029 assert_eq!(delivered, 1);
6030
6031 let result = store.cancel_scheduled(tid);
6033 assert!(result.is_err(), "Cannot cancel already-delivered message");
6034 }
6035
6036 #[test]
6037 fn temporal_cancel_nonexistent_error() {
6038 let mut store = CommStore::new();
6039 let result = store.cancel_scheduled(9999);
6040 assert!(result.is_err(), "Cancelling non-existent temporal ID should fail");
6041 }
6042
6043 #[test]
6044 fn temporal_deliver_future_relative_not_delivered() {
6045 let (mut store, ch_id) = new_store_with_channel();
6046 store
6047 .schedule_message(
6048 ch_id,
6049 "alice",
6050 "later msg",
6051 TemporalTarget::FutureRelative { delay_seconds: 3600 },
6052 None,
6053 )
6054 .unwrap();
6055
6056 let delivered = store.deliver_pending_temporal();
6058 assert_eq!(delivered, 0, "FutureRelative messages should not be delivered by deliver_pending_temporal");
6059 assert_eq!(store.list_scheduled().len(), 1, "Message should still be in queue");
6060 }
6061
6062 #[test]
6063 fn temporal_multiple_immediate() {
6064 let (mut store, ch_id) = new_store_with_channel();
6065 for i in 0..5 {
6066 store
6067 .schedule_message(
6068 ch_id,
6069 "alice",
6070 &format!("immediate-{i}"),
6071 TemporalTarget::Immediate,
6072 None,
6073 )
6074 .unwrap();
6075 }
6076 assert_eq!(store.list_scheduled().len(), 5);
6077
6078 let delivered = store.deliver_pending_temporal();
6079 assert_eq!(delivered, 5, "All 5 Immediate messages should be delivered");
6080 assert_eq!(store.list_scheduled().len(), 0, "No undelivered messages should remain");
6081 assert_eq!(store.messages.len(), 5);
6083 }
6084
6085 #[test]
6088 fn federation_empty_zone_error() {
6089 let mut store = CommStore::new();
6090 let result = store.configure_federation(true, "", FederationPolicy::Allow);
6091 assert!(result.is_err(), "Empty local_zone should return error");
6092 }
6093
6094 #[test]
6095 fn federation_remove_nonexistent_zone_error() {
6096 let mut store = CommStore::new();
6097 let result = store.remove_federated_zone("does-not-exist");
6098 assert!(result.is_err(), "Removing non-existent zone should return error");
6099 }
6100
6101 #[test]
6102 fn federation_zone_with_trust_level() {
6103 let mut store = CommStore::new();
6104 store
6105 .add_federated_zone(FederatedZone {
6106 zone_id: "trusted-zone".to_string(),
6107 name: "Trusted Zone".to_string(),
6108 endpoint: "https://trusted.example.com".to_string(),
6109 policy: FederationPolicy::Allow,
6110 trust_level: CommTrustLevel::Full,
6111 })
6112 .unwrap();
6113 let zones = store.list_federated_zones();
6114 assert_eq!(zones.len(), 1);
6115 assert_eq!(zones[0].trust_level, CommTrustLevel::Full);
6116 assert_eq!(zones[0].zone_id, "trusted-zone");
6117 assert_eq!(zones[0].policy, FederationPolicy::Allow);
6118 }
6119
6120 #[test]
6123 fn hive_form_empty_name_error() {
6124 let mut store = CommStore::new();
6125 let result = store.form_hive("", "alice", CollectiveDecisionMode::Majority);
6126 assert!(result.is_err(), "Empty hive name should return error");
6127 }
6128
6129 #[test]
6130 fn hive_dissolve_nonexistent_error() {
6131 let mut store = CommStore::new();
6132 let result = store.dissolve_hive(9999);
6133 assert!(result.is_err(), "Dissolving non-existent hive should return error");
6134 }
6135
6136 #[test]
6137 fn hive_leave_nonexistent_member_error() {
6138 let mut store = CommStore::new();
6139 let hive = store
6140 .form_hive("test-hive", "alice", CollectiveDecisionMode::Majority)
6141 .unwrap();
6142 let hid = hive.id;
6143 let result = store.leave_hive(hid, "ghost");
6144 assert!(result.is_err(), "Leaving hive when not a member should fail");
6145 }
6146
6147 #[test]
6148 fn hive_multiple_members() {
6149 let mut store = CommStore::new();
6150 let hive = store
6151 .form_hive("big-hive", "coordinator", CollectiveDecisionMode::Consensus)
6152 .unwrap();
6153 let hid = hive.id;
6154 assert_eq!(store.get_hive(hid).unwrap().constituents.len(), 1);
6156
6157 store.join_hive(hid, "agent-a", HiveRole::Member).unwrap();
6158 store.join_hive(hid, "agent-b", HiveRole::Member).unwrap();
6159 store.join_hive(hid, "agent-c", HiveRole::Observer).unwrap();
6160
6161 let hive = store.get_hive(hid).unwrap();
6162 assert_eq!(hive.constituents.len(), 4, "Should have coordinator + 3 members");
6163 assert_eq!(hive.constituents[0].role, HiveRole::Coordinator);
6165 assert_eq!(hive.constituents[1].role, HiveRole::Member);
6166 assert_eq!(hive.constituents[3].role, HiveRole::Observer);
6167 }
6168
6169 #[test]
6172 fn grounding_message_content() {
6173 let (mut store, ch_id) = new_store_with_channel();
6174 store
6175 .send_message(ch_id, "alice", "the deployment succeeded", MessageType::Text)
6176 .unwrap();
6177 let result = store.ground_claim("alice sent a message");
6179 assert_ne!(result.status, GroundingStatus::Ungrounded);
6180 assert!(
6181 result.evidence.iter().any(|e| e.evidence_type == "message"),
6182 "Should have message evidence"
6183 );
6184 }
6185
6186 #[test]
6187 fn grounding_hive_name() {
6188 let mut store = CommStore::new();
6189 store
6190 .form_hive("project-alpha", "alice", CollectiveDecisionMode::Majority)
6191 .unwrap();
6192 let result = store.ground_claim("project-alpha hive exists");
6193 assert_eq!(result.status, GroundingStatus::Verified);
6194 assert!(
6195 result.evidence.iter().any(|e| e.evidence_type == "hive"),
6196 "Should have hive evidence"
6197 );
6198 }
6199
6200 #[test]
6201 fn grounding_consent_evidence() {
6202 let mut store = CommStore::new();
6203 store
6204 .grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None)
6205 .unwrap();
6206 let result = store.ground_claim("alice has granted consent");
6207 assert_ne!(result.status, GroundingStatus::Ungrounded);
6208 assert!(
6209 result.evidence.iter().any(|e| e.evidence_type == "consent"),
6210 "Should have consent evidence"
6211 );
6212 }
6213
6214 #[test]
6217 fn comm_log_with_affect() {
6218 let mut store = CommStore::new();
6219 let affect = AffectState {
6220 valence: 0.7,
6221 arousal: 0.5,
6222 dominance: 0.6,
6223 emotions: vec![Emotion::Joy, Emotion::Excitement],
6224 urgency: UrgencyLevel::High,
6225 meta_confidence: 0.9,
6226 };
6227 let entry = store.log_communication(
6228 "Great progress today!",
6229 "agent",
6230 Some("status-update".to_string()),
6231 None,
6232 Some(affect),
6233 );
6234 assert_eq!(entry.content, "Great progress today!");
6235 assert_eq!(entry.role, "agent");
6236 assert_eq!(entry.topic.as_deref(), Some("status-update"));
6237 assert!(entry.affect.is_some());
6238 let stored_affect = entry.affect.as_ref().unwrap();
6239 assert_eq!(stored_affect.valence, 0.7);
6240 assert_eq!(stored_affect.emotions.len(), 2);
6241 }
6242
6243 #[test]
6244 fn comm_log_limit() {
6245 let mut store = CommStore::new();
6246 for i in 0..10 {
6247 store.log_communication(
6248 &format!("entry-{i}"),
6249 "user",
6250 None,
6251 None,
6252 None,
6253 );
6254 }
6255 assert_eq!(store.get_comm_log(None).len(), 10, "All 10 entries should be present");
6256 let last_3 = store.get_comm_log(Some(3));
6257 assert_eq!(last_3.len(), 3, "Should return last 3 entries");
6258 assert_eq!(last_3[0].content, "entry-7");
6259 assert_eq!(last_3[1].content, "entry-8");
6260 assert_eq!(last_3[2].content, "entry-9");
6261 }
6262
6263 #[test]
6266 fn stats_comprehensive() {
6267 let mut store = CommStore::new();
6268
6269 let ch1 = store.create_channel("chan-1", ChannelType::Group, None).unwrap();
6271 let ch2 = store.create_channel("chan-2", ChannelType::Direct, None).unwrap();
6272 store.join_channel(ch1.id, "alice").unwrap();
6273 store.join_channel(ch1.id, "bob").unwrap();
6274 store.join_channel(ch2.id, "carol").unwrap();
6275
6276 store.send_message(ch1.id, "alice", "msg1", MessageType::Text).unwrap();
6278 store.send_message(ch1.id, "bob", "msg2", MessageType::Command).unwrap();
6279 store.send_message(ch2.id, "carol", "msg3", MessageType::Query).unwrap();
6280
6281 store.grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None).unwrap();
6283 store.grant_consent("bob", "carol", ConsentScope::SendMessages, None, None).unwrap();
6284
6285 store.set_trust_level("alice", CommTrustLevel::High).unwrap();
6287
6288 store.form_hive("stats-hive", "alice", CollectiveDecisionMode::Majority).unwrap();
6290
6291 store.schedule_message(ch1.id, "alice", "future", TemporalTarget::FutureRelative { delay_seconds: 100 }, None).unwrap();
6293
6294 store.configure_federation(true, "local-zone", FederationPolicy::Allow).unwrap();
6296 store.add_federated_zone(FederatedZone {
6297 zone_id: "remote".to_string(),
6298 name: "Remote Zone".to_string(),
6299 endpoint: String::new(),
6300 policy: FederationPolicy::Selective,
6301 trust_level: CommTrustLevel::Basic,
6302 }).unwrap();
6303
6304 store.log_communication("log1", "user", None, None, None);
6306 store.log_communication("log2", "agent", None, None, None);
6307
6308 let stats = store.stats();
6309 assert_eq!(stats.channel_count, 2);
6310 assert_eq!(stats.message_count, 3);
6311 assert_eq!(stats.total_participants, 3); assert_eq!(stats.consent_gate_count, 2);
6313 assert_eq!(stats.trust_override_count, 1);
6314 assert_eq!(stats.hive_count, 1);
6315 assert_eq!(stats.temporal_queue_count, 1);
6316 assert!(stats.federation_enabled);
6317 assert_eq!(stats.federated_zone_count, 1);
6318 assert_eq!(stats.comm_log_count, 2);
6319 assert_eq!(stats.dead_letter_count, 0);
6320 assert_eq!(stats.messages_by_type.get("text"), Some(&1));
6322 assert_eq!(stats.messages_by_type.get("command"), Some(&1));
6323 assert_eq!(stats.messages_by_type.get("query"), Some(&1));
6324 assert_eq!(stats.channels_by_state.get("active"), Some(&2));
6326 assert!(stats.oldest_message.is_some());
6328 assert!(stats.newest_message.is_some());
6329 }
6330
6331 #[test]
6334 fn save_load_consent_roundtrip() {
6335 let mut store = CommStore::new();
6336 store
6337 .grant_consent(
6338 "alice",
6339 "bob",
6340 ConsentScope::Federate,
6341 Some("federation partnership".to_string()),
6342 Some("2030-06-15T00:00:00Z".to_string()),
6343 )
6344 .unwrap();
6345 store
6346 .grant_consent("carol", "dave", ConsentScope::HiveParticipation, None, None)
6347 .unwrap();
6348
6349 let dir = tempfile::tempdir().unwrap();
6350 let path = dir.path().join("consent_roundtrip.acomm");
6351 store.save(&path).unwrap();
6352
6353 let loaded = CommStore::load(&path).unwrap();
6354 assert_eq!(loaded.consent_gates.len(), 2);
6355 assert!(loaded.check_consent("alice", "bob", &ConsentScope::Federate));
6356 assert!(loaded.check_consent("carol", "dave", &ConsentScope::HiveParticipation));
6357 let alice_gate = loaded
6359 .consent_gates
6360 .iter()
6361 .find(|e| e.grantor == "alice" && e.grantee == "bob")
6362 .unwrap();
6363 assert_eq!(alice_gate.reason.as_deref(), Some("federation partnership"));
6364 assert_eq!(alice_gate.expires_at.as_deref(), Some("2030-06-15T00:00:00Z"));
6365 }
6366
6367 #[test]
6368 fn save_load_trust_roundtrip() {
6369 let mut store = CommStore::new();
6370 store.set_trust_level("agent-a", CommTrustLevel::Absolute).unwrap();
6371 store.set_trust_level("agent-b", CommTrustLevel::None).unwrap();
6372 store.set_trust_level("agent-c", CommTrustLevel::Minimal).unwrap();
6373
6374 let dir = tempfile::tempdir().unwrap();
6375 let path = dir.path().join("trust_roundtrip.acomm");
6376 store.save(&path).unwrap();
6377
6378 let loaded = CommStore::load(&path).unwrap();
6379 assert_eq!(loaded.list_trust_levels().len(), 3);
6380 assert_eq!(loaded.get_trust_level("agent-a"), CommTrustLevel::Absolute);
6381 assert_eq!(loaded.get_trust_level("agent-b"), CommTrustLevel::None);
6382 assert_eq!(loaded.get_trust_level("agent-c"), CommTrustLevel::Minimal);
6383 assert_eq!(loaded.get_trust_level("unknown"), CommTrustLevel::Standard);
6385 }
6386
6387 #[test]
6388 fn save_load_hive_roundtrip() {
6389 let mut store = CommStore::new();
6390 let hive = store
6391 .form_hive("persistent-hive", "coordinator", CollectiveDecisionMode::Unanimous)
6392 .unwrap();
6393 let hid = hive.id;
6394 store.join_hive(hid, "member-a", HiveRole::Member).unwrap();
6395 store.join_hive(hid, "observer-b", HiveRole::Observer).unwrap();
6396
6397 let dir = tempfile::tempdir().unwrap();
6398 let path = dir.path().join("hive_roundtrip.acomm");
6399 store.save(&path).unwrap();
6400
6401 let loaded = CommStore::load(&path).unwrap();
6402 assert_eq!(loaded.hive_minds.len(), 1);
6403 let loaded_hive = loaded.get_hive(hid).unwrap();
6404 assert_eq!(loaded_hive.name, "persistent-hive");
6405 assert_eq!(loaded_hive.constituents.len(), 3);
6406 assert_eq!(loaded_hive.decision_mode, CollectiveDecisionMode::Unanimous);
6407 assert_eq!(loaded_hive.constituents[0].role, HiveRole::Coordinator);
6408 assert_eq!(loaded_hive.constituents[0].agent_id, "coordinator");
6409 assert_eq!(loaded_hive.constituents[1].agent_id, "member-a");
6410 assert_eq!(loaded_hive.constituents[2].agent_id, "observer-b");
6411 }
6412
6413 #[test]
6414 fn save_load_temporal_roundtrip() {
6415 let mut store = CommStore::new();
6416 let ch = store.create_channel("temporal-ch", ChannelType::Group, None).unwrap();
6417 store
6418 .schedule_message(
6419 ch.id,
6420 "alice",
6421 "deliver later",
6422 TemporalTarget::FutureAbsolute {
6423 deliver_at: "2030-01-01T00:00:00Z".to_string(),
6424 },
6425 Some(AffectState {
6426 valence: 0.5,
6427 arousal: 0.3,
6428 ..Default::default()
6429 }),
6430 )
6431 .unwrap();
6432 store
6433 .schedule_message(ch.id, "bob", "deliver now", TemporalTarget::Immediate, None)
6434 .unwrap();
6435
6436 let dir = tempfile::tempdir().unwrap();
6437 let path = dir.path().join("temporal_roundtrip.acomm");
6438 store.save(&path).unwrap();
6439
6440 let loaded = CommStore::load(&path).unwrap();
6441 assert_eq!(loaded.temporal_queue.len(), 2);
6442 assert_eq!(loaded.list_scheduled().len(), 2);
6444 let first = &loaded.temporal_queue[0];
6446 assert_eq!(first.sender, "alice");
6447 assert_eq!(first.content, "deliver later");
6448 assert!(first.affect.is_some());
6449 assert!(!first.delivered);
6450 let second = &loaded.temporal_queue[1];
6452 assert_eq!(second.sender, "bob");
6453 assert!(second.affect.is_none());
6454 }
6455
6456 #[test]
6459 fn channel_state_new_variants_display() {
6460 assert_eq!(ChannelState::Archived.to_string(), "archived");
6461 assert_eq!(ChannelState::SilentCommunion.to_string(), "silent_communion");
6462 assert_eq!(ChannelState::HiveMode.to_string(), "hive_mode");
6463 assert_eq!(ChannelState::PendingConsent.to_string(), "pending_consent");
6464 }
6465
6466 #[test]
6467 fn channel_state_archived_blocks_send_allows_receive() {
6468 let mut store = CommStore::new();
6469 let ch = store.create_channel("arch-ch", ChannelType::Group, None).unwrap();
6470 let ch_id = ch.id;
6471 store.join_channel(ch_id, "alice").unwrap();
6472 store.channels.get_mut(&ch_id).unwrap().state = ChannelState::Archived;
6474 assert!(store.send_message(ch_id, "alice", "hello", MessageType::Text).is_err());
6476 }
6477
6478 #[test]
6479 fn channel_state_pending_consent_blocks_both() {
6480 let mut store = CommStore::new();
6481 let ch = store.create_channel("pc-ch", ChannelType::Group, None).unwrap();
6482 let ch_id = ch.id;
6483 store.join_channel(ch_id, "alice").unwrap();
6484 store.channels.get_mut(&ch_id).unwrap().state = ChannelState::PendingConsent;
6485 assert!(store.send_message(ch_id, "alice", "hello", MessageType::Text).is_err());
6486 }
6487
6488 #[test]
6491 fn comm_timestamp_increment_and_merge() {
6492 let mut ts = CommTimestamp::now("a");
6493 ts.increment("a");
6494 assert_eq!(ts.lamport, 1);
6495 assert_eq!(ts.vector_clock["a"], 1);
6496
6497 let mut ts2 = CommTimestamp::now("b");
6498 ts2.increment("b");
6499 ts2.increment("b");
6500
6501 ts.merge(&ts2, "a");
6502 assert_eq!(ts.lamport, 3); assert_eq!(ts.vector_clock["a"], 2); assert_eq!(ts.vector_clock["b"], 2);
6505 }
6506
6507 #[test]
6508 fn comm_timestamp_happens_before_basic() {
6509 let mut a = CommTimestamp::now("x");
6510 a.increment("x");
6511 let mut b = a.clone();
6512 b.increment("x");
6513 assert!(a.happens_before(&b));
6514 assert!(!b.happens_before(&a));
6515 let c = a.clone();
6517 assert!(!a.happens_before(&c));
6518 }
6519
6520 #[test]
6523 fn audit_log_and_retrieve() {
6524 let mut store = CommStore::new();
6525 store.log_audit(AuditEventType::ChannelCreated, "agent-1", "Created general", Some("1".to_string()));
6526 store.log_audit(AuditEventType::MessageSent, "agent-2", "Sent hello", None);
6527 store.log_audit(AuditEventType::ConsentGranted, "agent-1", "Granted read to agent-2", Some("consent-1".to_string()));
6528
6529 let all = store.get_audit_log(None);
6530 assert_eq!(all.len(), 3);
6531
6532 let last2 = store.get_audit_log(Some(2));
6533 assert_eq!(last2.len(), 2);
6534 assert_eq!(last2[0].description, "Sent hello");
6535 assert_eq!(last2[1].description, "Granted read to agent-2");
6536 }
6537
6538 #[test]
6539 fn audit_log_in_stats() {
6540 let mut store = CommStore::new();
6541 store.log_audit(AuditEventType::AuthFailure, "attacker", "Bad credentials", None);
6542 store.log_audit(AuditEventType::RateLimitExceeded, "spammer", "Too many messages", None);
6543 let stats = store.stats();
6544 assert_eq!(stats.audit_log_count, 2);
6545 }
6546
6547 #[test]
6550 fn rate_limit_config_defaults_in_store() {
6551 let store = CommStore::new();
6552 assert_eq!(store.rate_limit_config.messages_per_minute, 60);
6553 assert_eq!(store.rate_limit_config.semantic_per_minute, 10);
6554 assert_eq!(store.rate_limit_config.affect_per_minute, 30);
6555 assert_eq!(store.rate_limit_config.hive_per_hour, 5);
6556 assert_eq!(store.rate_limit_config.federation_per_minute, 20);
6557 }
6558
6559 #[test]
6564 fn consent_enforcement_blocks_semantic_without_consent() {
6565 let mut store = CommStore::new();
6566 let ch = store
6567 .create_channel("consent-test", ChannelType::Group, None)
6568 .unwrap();
6569 store.join_channel(ch.id, "sender-agent").unwrap();
6570 store.join_channel(ch.id, "receiver-agent").unwrap();
6571
6572 let result = store.send_message(
6574 ch.id,
6575 "sender-agent",
6576 "[affect: joy intensity=0.8] Great news!",
6577 MessageType::Text,
6578 );
6579
6580 assert!(result.is_err(), "Affect-enriched message should be blocked without consent");
6581 match result.unwrap_err() {
6582 CommError::ConsentDenied { reason } => {
6583 assert!(
6584 reason.contains("receiver-agent"),
6585 "Error should mention the participant who hasn't granted consent"
6586 );
6587 assert!(
6588 reason.contains("SendMessages"),
6589 "Error should mention the required consent scope"
6590 );
6591 }
6592 other => panic!("Expected ConsentDenied, got: {:?}", other),
6593 }
6594 }
6595
6596 #[test]
6597 fn consent_enforcement_allows_with_grant() {
6598 let mut store = CommStore::new();
6599 let ch = store
6600 .create_channel("consent-allow-test", ChannelType::Group, None)
6601 .unwrap();
6602 store.join_channel(ch.id, "sender-agent").unwrap();
6603 store.join_channel(ch.id, "receiver-agent").unwrap();
6604
6605 store
6607 .grant_consent(
6608 "receiver-agent",
6609 "sender-agent",
6610 ConsentScope::SendMessages,
6611 Some("Allow affect messages".to_string()),
6612 None,
6613 )
6614 .unwrap();
6615
6616 let result = store.send_message(
6618 ch.id,
6619 "sender-agent",
6620 "[affect: joy intensity=0.8] Great news!",
6621 MessageType::Text,
6622 );
6623
6624 assert!(
6625 result.is_ok(),
6626 "Affect-enriched message should be allowed after consent grant"
6627 );
6628
6629 let result2 = store.send_message(
6631 ch.id,
6632 "sender-agent",
6633 "Plain text message",
6634 MessageType::Text,
6635 );
6636 assert!(result2.is_ok(), "Plain text should always be allowed");
6637 }
6638
6639 #[test]
6644 fn rate_limiting_blocks_after_threshold() {
6645 let mut store = CommStore::new();
6646 store.rate_limit_config.messages_per_minute = 3;
6648
6649 let ch = store
6650 .create_channel("rate-test", ChannelType::Group, None)
6651 .unwrap();
6652 store.join_channel(ch.id, "fast-sender").unwrap();
6653
6654 for i in 0..3 {
6656 let result = store.send_message(
6657 ch.id,
6658 "fast-sender",
6659 &format!("Message {}", i),
6660 MessageType::Text,
6661 );
6662 assert!(result.is_ok(), "Message {} should succeed (within limit)", i);
6663 }
6664
6665 let result = store.send_message(
6667 ch.id,
6668 "fast-sender",
6669 "One too many",
6670 MessageType::Text,
6671 );
6672 assert!(result.is_err(), "4th message should be rate-limited");
6673 match result.unwrap_err() {
6674 CommError::RateLimitExceeded { limit } => {
6675 assert!(
6676 limit.contains("fast-sender"),
6677 "Error should mention the sender"
6678 );
6679 assert!(
6680 limit.contains("3"),
6681 "Error should mention the limit threshold"
6682 );
6683 }
6684 other => panic!("Expected RateLimitExceeded, got: {:?}", other),
6685 }
6686
6687 store.join_channel(ch.id, "other-sender").unwrap();
6689 let result = store.send_message(
6690 ch.id,
6691 "other-sender",
6692 "I can still send",
6693 MessageType::Text,
6694 );
6695 assert!(
6696 result.is_ok(),
6697 "Different sender should have independent rate limit"
6698 );
6699 }
6700
6701 #[test]
6702 fn rate_limiting_resets_after_window() {
6703 let mut store = CommStore::new();
6704 store.rate_limit_config.messages_per_minute = 2;
6705
6706 let ch = store
6707 .create_channel("rate-reset-test", ChannelType::Group, None)
6708 .unwrap();
6709 store.join_channel(ch.id, "resetting-sender").unwrap();
6710
6711 store
6713 .send_message(ch.id, "resetting-sender", "msg1", MessageType::Text)
6714 .unwrap();
6715 store
6716 .send_message(ch.id, "resetting-sender", "msg2", MessageType::Text)
6717 .unwrap();
6718
6719 let blocked = store.send_message(
6721 ch.id,
6722 "resetting-sender",
6723 "blocked",
6724 MessageType::Text,
6725 );
6726 assert!(blocked.is_err(), "Should be rate-limited");
6727
6728 if let Some(tracker) = store.rate_trackers.get_mut("resetting-sender") {
6731 tracker.last_minute_reset = tracker.last_minute_reset.saturating_sub(61);
6732 }
6733
6734 let result = store.send_message(
6736 ch.id,
6737 "resetting-sender",
6738 "after reset",
6739 MessageType::Text,
6740 );
6741 assert!(
6742 result.is_ok(),
6743 "Message should succeed after rate limit window resets"
6744 );
6745 }
6746
6747 #[test]
6752 fn audit_log_records_operations() {
6753 let mut store = CommStore::new();
6754 assert!(store.audit_log.is_empty(), "Audit log should start empty");
6755
6756 let ch = store
6758 .create_channel("audit-test", ChannelType::Group, None)
6759 .unwrap();
6760 let channel_created_count = store
6761 .audit_log
6762 .iter()
6763 .filter(|e| e.event_type == AuditEventType::ChannelCreated)
6764 .count();
6765 assert_eq!(channel_created_count, 1, "Should have one ChannelCreated audit entry");
6766
6767 store.join_channel(ch.id, "audit-agent").unwrap();
6769 store
6770 .send_message(ch.id, "audit-agent", "hello", MessageType::Text)
6771 .unwrap();
6772 let msg_sent_count = store
6773 .audit_log
6774 .iter()
6775 .filter(|e| e.event_type == AuditEventType::MessageSent)
6776 .count();
6777 assert_eq!(msg_sent_count, 1, "Should have one MessageSent audit entry");
6778
6779 store
6781 .grant_consent(
6782 "grantor-a",
6783 "grantee-b",
6784 ConsentScope::ReadMessages,
6785 None,
6786 None,
6787 )
6788 .unwrap();
6789 let consent_granted_count = store
6790 .audit_log
6791 .iter()
6792 .filter(|e| e.event_type == AuditEventType::ConsentGranted)
6793 .count();
6794 assert_eq!(consent_granted_count, 1, "Should have one ConsentGranted audit entry");
6795
6796 let _ = store.revoke_consent("grantor-a", "grantee-b", &ConsentScope::ReadMessages);
6798 let consent_revoked_count = store
6799 .audit_log
6800 .iter()
6801 .filter(|e| e.event_type == AuditEventType::ConsentRevoked)
6802 .count();
6803 assert_eq!(consent_revoked_count, 1, "Should have one ConsentRevoked audit entry");
6804
6805 store.close_channel(ch.id).unwrap();
6807 let channel_closed_count = store
6808 .audit_log
6809 .iter()
6810 .filter(|e| e.event_type == AuditEventType::ChannelClosed)
6811 .count();
6812 assert_eq!(channel_closed_count, 1, "Should have one ChannelClosed audit entry");
6813
6814 assert!(
6816 store.audit_log.len() >= 5,
6817 "Audit log should have at least 5 entries, got {}",
6818 store.audit_log.len()
6819 );
6820 }
6821
6822 #[test]
6827 fn signature_verification_detects_tampering() {
6828 let mut store = CommStore::new();
6829 let ch = store
6830 .create_channel("sig-test", ChannelType::Group, None)
6831 .unwrap();
6832 store.join_channel(ch.id, "agent-sig").unwrap();
6833
6834 let msg = store
6835 .send_message(ch.id, "agent-sig", "Original content", MessageType::Text)
6836 .unwrap();
6837 let msg_id = msg.id;
6838
6839 assert!(
6841 store.verify_message_signature(msg_id),
6842 "Untampered message should pass signature verification"
6843 );
6844
6845 if let Some(message) = store.messages.get_mut(&msg_id) {
6847 message.content = "Tampered content".to_string();
6848 }
6849
6850 let audit_len_before = store.audit_log.len();
6852 let result = store.verify_message_signature(msg_id);
6853 assert!(
6854 !result,
6855 "Tampered message should fail signature verification"
6856 );
6857
6858 let sig_warnings = store
6860 .audit_log
6861 .iter()
6862 .skip(audit_len_before)
6863 .filter(|e| e.event_type == AuditEventType::SignatureWarning)
6864 .count();
6865 assert_eq!(
6866 sig_warnings, 1,
6867 "Should have logged a SignatureWarning audit entry for the tampered message"
6868 );
6869 }
6870
6871 #[test]
6874 fn file_locking_exclusive_blocks_second_try() {
6875 let dir = tempfile::tempdir().unwrap();
6876 let data_path = dir.path().join("test.acomm");
6877
6878 let lock1 = CommFileLock::acquire(&data_path).unwrap();
6880
6881 let result = CommFileLock::try_acquire(&data_path);
6883 assert!(
6884 result.is_err(),
6885 "try_acquire should fail while exclusive lock is held"
6886 );
6887
6888 lock1.release().unwrap();
6890 }
6891
6892 #[test]
6893 fn file_locking_release_allows_reacquire() {
6894 let dir = tempfile::tempdir().unwrap();
6895 let data_path = dir.path().join("test.acomm");
6896
6897 let lock1 = CommFileLock::acquire(&data_path).unwrap();
6899 lock1.release().unwrap();
6900
6901 let lock2 = CommFileLock::acquire(&data_path).unwrap();
6903 lock2.release().unwrap();
6904 }
6905
6906 #[test]
6907 fn file_locking_stale_recovery() {
6908 let dir = tempfile::tempdir().unwrap();
6909 let data_path = dir.path().join("test.acomm");
6910 let lock_path = data_path.with_extension("acomm.lock");
6911
6912 std::fs::File::create(&lock_path).unwrap();
6914
6915 let old_time = FileTime::from_unix_time(
6917 std::time::SystemTime::now()
6918 .duration_since(std::time::UNIX_EPOCH)
6919 .unwrap()
6920 .as_secs() as i64
6921 - 120,
6922 0,
6923 );
6924 filetime::set_file_mtime(&lock_path, old_time).unwrap();
6925
6926 let recovered = CommFileLock::recover_stale(&data_path, 60).unwrap();
6928 assert!(recovered, "Should have recovered stale lock file");
6929 assert!(
6930 !lock_path.exists(),
6931 "Stale lock file should have been removed"
6932 );
6933 }
6934
6935 #[test]
6936 fn file_locking_stale_recovery_not_stale() {
6937 let dir = tempfile::tempdir().unwrap();
6938 let data_path = dir.path().join("test.acomm");
6939 let lock_path = data_path.with_extension("acomm.lock");
6940
6941 std::fs::File::create(&lock_path).unwrap();
6943
6944 let recovered = CommFileLock::recover_stale(&data_path, 3600).unwrap();
6946 assert!(!recovered, "Fresh lock should not be recovered");
6947 assert!(lock_path.exists(), "Fresh lock file should still exist");
6948 }
6949
6950 #[test]
6951 fn file_locking_no_lock_file_recovery() {
6952 let dir = tempfile::tempdir().unwrap();
6953 let data_path = dir.path().join("test.acomm");
6954
6955 let recovered = CommFileLock::recover_stale(&data_path, 0).unwrap();
6957 assert!(!recovered, "No lock file means nothing to recover");
6958 }
6959
6960 #[test]
6961 fn file_locking_save_load_with_locks() {
6962 let dir = tempfile::tempdir().unwrap();
6963 let path = dir.path().join("locked.acomm");
6964
6965 let mut store = CommStore::new();
6967 store
6968 .create_channel("lock-test", ChannelType::Group, None)
6969 .unwrap();
6970 store
6971 .send_message(1, "agent-a", "hello from lock test", MessageType::Text)
6972 .unwrap();
6973
6974 store.save(&path).unwrap();
6976
6977 let loaded = CommStore::load(&path).unwrap();
6979 assert_eq!(loaded.channels.len(), 1);
6980 assert_eq!(loaded.messages.len(), 1);
6981
6982 let msg = loaded.messages.values().next().unwrap();
6983 assert_eq!(msg.content, "hello from lock test");
6984 }
6985
6986 #[test]
6987 fn file_locking_shared_allows_multiple_readers() {
6988 let dir = tempfile::tempdir().unwrap();
6989 let data_path = dir.path().join("test.acomm");
6990
6991 let lock1 = CommFileLock::acquire_shared(&data_path).unwrap();
6993 let lock2 = CommFileLock::acquire_shared(&data_path).unwrap();
6994
6995 lock1.release().unwrap();
6997 lock2.release().unwrap();
6998 }
6999
7000 #[test]
7001 fn file_locking_drop_releases_lock() {
7002 let dir = tempfile::tempdir().unwrap();
7003 let data_path = dir.path().join("test.acomm");
7004
7005 {
7007 let _lock = CommFileLock::acquire(&data_path).unwrap();
7008 }
7010
7011 let lock2 = CommFileLock::try_acquire(&data_path).unwrap();
7013 lock2.release().unwrap();
7014 }
7015
7016
7017 #[test]
7020 fn affect_contagion_basic() {
7021 let mut store = CommStore::new();
7022 let ch = store
7023 .create_channel("affect-test", ChannelType::Group, None)
7024 .unwrap();
7025 store.join_channel(ch.id, "alice").unwrap();
7026 store.join_channel(ch.id, "bob").unwrap();
7027
7028 let msg = store
7030 .send_message(ch.id, "alice", "I am so happy!", MessageType::Text)
7031 .unwrap();
7032 store
7034 .messages
7035 .get_mut(&msg.id)
7036 .unwrap()
7037 .metadata
7038 .insert("valence".to_string(), "0.9".to_string());
7039 store
7040 .messages
7041 .get_mut(&msg.id)
7042 .unwrap()
7043 .metadata
7044 .insert("arousal".to_string(), "0.7".to_string());
7045 store
7046 .messages
7047 .get_mut(&msg.id)
7048 .unwrap()
7049 .metadata
7050 .insert("dominance".to_string(), "0.6".to_string());
7051
7052 store.set_affect_resistance(0.0);
7053 let results = store.process_affect_contagion(ch.id);
7054
7055 assert!(!results.is_empty());
7057 let bob_result = results.iter().find(|(name, _, _, _)| name == "bob");
7058 assert!(bob_result.is_some());
7059 let (_, valence, arousal, _) = bob_result.unwrap();
7060 assert!(*valence > 0.0, "Bob's valence should be positive");
7061 assert!(*arousal > 0.0, "Bob's arousal should be positive");
7062 }
7063
7064 #[test]
7065 fn affect_contagion_empty_channel() {
7066 let mut store = CommStore::new();
7067 let results = store.process_affect_contagion(999);
7069 assert!(results.is_empty());
7070 }
7071
7072 #[test]
7073 fn affect_history_empty() {
7074 let store = CommStore::new();
7075 let history = store.get_affect_history("nonexistent-agent");
7076 assert_eq!(history.agent, "nonexistent-agent");
7077 assert!(history.states.is_empty());
7078 }
7079
7080 #[test]
7081 fn affect_history_with_state() {
7082 let mut store = CommStore::new();
7083 store.affect_states.insert(
7084 "agent-x".to_string(),
7085 AffectState {
7086 valence: 0.5,
7087 arousal: 0.3,
7088 dominance: 0.7,
7089 ..AffectState::default()
7090 },
7091 );
7092 let history = store.get_affect_history("agent-x");
7093 assert_eq!(history.agent, "agent-x");
7094 assert!(!history.states.is_empty());
7095 let last = history.states.last().unwrap();
7096 assert_eq!(last.source, "current");
7097 assert!((last.valence - 0.5).abs() < 0.01);
7098 }
7099
7100 #[test]
7101 fn affect_decay_reduces_state() {
7102 let mut store = CommStore::new();
7103 store.affect_states.insert(
7104 "agent-y".to_string(),
7105 AffectState {
7106 valence: 0.8,
7107 arousal: 0.6,
7108 dominance: 0.5,
7109 ..AffectState::default()
7110 },
7111 );
7112 store.apply_affect_decay(0.5);
7113 let state = store.affect_states.get("agent-y").unwrap();
7114 assert!((state.valence - 0.4).abs() < 0.01, "valence should halve");
7115 assert!((state.arousal - 0.3).abs() < 0.01, "arousal should halve");
7116 }
7117
7118 #[test]
7119 fn forward_message_basic() {
7120 let mut store = CommStore::new();
7121 let ch1 = store
7122 .create_channel("source-chan", ChannelType::Group, None)
7123 .unwrap();
7124 let ch2 = store
7125 .create_channel("target-chan", ChannelType::Group, None)
7126 .unwrap();
7127 store.join_channel(ch1.id, "alice").unwrap();
7128 store.join_channel(ch2.id, "bob").unwrap();
7129
7130 let msg = store
7131 .send_message(ch1.id, "alice", "Hello world", MessageType::Text)
7132 .unwrap();
7133
7134 let fwd_id = store
7135 .forward_message(msg.id, ch2.id, "bob")
7136 .unwrap();
7137
7138 let fwd_msg = store.messages.get(&fwd_id).unwrap();
7139 assert!(fwd_msg.content.starts_with("[Forwarded]"));
7140 assert_eq!(fwd_msg.channel_id, ch2.id);
7141 assert_eq!(
7142 fwd_msg.metadata.get("forwarded_from").unwrap(),
7143 &msg.id.to_string()
7144 );
7145 assert_eq!(fwd_msg.metadata.get("echo_depth").unwrap(), "1");
7146 }
7147
7148 #[test]
7149 fn forward_message_not_found() {
7150 let mut store = CommStore::new();
7151 let ch = store
7152 .create_channel("target", ChannelType::Group, None)
7153 .unwrap();
7154 let result = store.forward_message(999, ch.id, "bob");
7155 assert!(result.is_err());
7156 assert!(result.unwrap_err().contains("not found"));
7157 }
7158
7159 #[test]
7160 fn forward_message_target_not_found() {
7161 let mut store = CommStore::new();
7162 let ch = store
7163 .create_channel("source", ChannelType::Group, None)
7164 .unwrap();
7165 store.join_channel(ch.id, "alice").unwrap();
7166 let msg = store
7167 .send_message(ch.id, "alice", "test", MessageType::Text)
7168 .unwrap();
7169 let result = store.forward_message(msg.id, 999, "bob");
7170 assert!(result.is_err());
7171 assert!(result.unwrap_err().contains("not found"));
7172 }
7173
7174 #[test]
7175 fn echo_chain_and_depth() {
7176 let mut store = CommStore::new();
7177 let ch1 = store
7178 .create_channel("ch1", ChannelType::Group, None)
7179 .unwrap();
7180 let ch2 = store
7181 .create_channel("ch2", ChannelType::Group, None)
7182 .unwrap();
7183 let ch3 = store
7184 .create_channel("ch3", ChannelType::Group, None)
7185 .unwrap();
7186 store.join_channel(ch1.id, "alice").unwrap();
7187 store.join_channel(ch2.id, "bob").unwrap();
7188 store.join_channel(ch3.id, "charlie").unwrap();
7189
7190 let orig = store
7191 .send_message(ch1.id, "alice", "Original", MessageType::Text)
7192 .unwrap();
7193 assert_eq!(store.get_echo_depth(orig.id), 0);
7194
7195 let fwd1 = store.forward_message(orig.id, ch2.id, "bob").unwrap();
7196 assert_eq!(store.get_echo_depth(fwd1), 1);
7197
7198 let fwd2 = store.forward_message(fwd1, ch3.id, "charlie").unwrap();
7199 assert_eq!(store.get_echo_depth(fwd2), 2);
7200
7201 let chain = store.query_echo_chain(fwd2);
7202 assert!(chain.len() >= 3);
7203 assert_eq!(chain[0].message_id, orig.id);
7204 assert_eq!(chain[0].depth, 0);
7205 }
7206
7207 #[test]
7208 fn summarize_conversation_basic() {
7209 let mut store = CommStore::new();
7210 let ch = store
7211 .create_channel("summary-test", ChannelType::Group, None)
7212 .unwrap();
7213 store.join_channel(ch.id, "alice").unwrap();
7214 store.join_channel(ch.id, "bob").unwrap();
7215
7216 store
7217 .send_message(ch.id, "alice", "Hello!", MessageType::Text)
7218 .unwrap();
7219 store
7220 .send_message(ch.id, "bob", "Hi there!", MessageType::Text)
7221 .unwrap();
7222 store
7223 .send_message(ch.id, "alice", "How are you?", MessageType::Text)
7224 .unwrap();
7225
7226 let summary = store.summarize_conversation(ch.id).unwrap();
7227 assert_eq!(summary.channel_id, ch.id);
7228 assert_eq!(summary.channel_name, "summary-test");
7229 assert_eq!(summary.message_count, 3);
7230 assert_eq!(summary.participant_count, 2);
7231 assert!(summary.avg_message_length > 0.0);
7232 assert!(!summary.has_affect_data);
7233 }
7234
7235 #[test]
7236 fn summarize_conversation_not_found() {
7237 let store = CommStore::new();
7238 let result = store.summarize_conversation(999);
7239 assert!(result.is_err());
7240 }
7241
7242 #[test]
7247 fn test_assign_comm_ids_fills_missing() {
7248 let mut store = CommStore::new();
7249 let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7250 store.send_message(ch_id, "alice", "hello", MessageType::Text).unwrap();
7251 store.send_message(ch_id, "bob", "world", MessageType::Text).unwrap();
7252
7253 for msg in store.messages.values() {
7255 assert!(msg.comm_id.is_none());
7256 }
7257 for chan in store.channels.values() {
7258 assert!(chan.comm_id.is_none());
7259 }
7260
7261 store.assign_comm_ids();
7262
7263 for msg in store.messages.values() {
7265 assert!(msg.comm_id.is_some());
7266 }
7267 for chan in store.channels.values() {
7268 assert!(chan.comm_id.is_some());
7269 }
7270 }
7271
7272 #[test]
7273 fn test_assign_comm_ids_idempotent() {
7274 let mut store = CommStore::new();
7275 let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7276 store.send_message(ch_id, "alice", "hello", MessageType::Text).unwrap();
7277
7278 store.assign_comm_ids();
7279 let first_id = store.messages.values().next().unwrap().comm_id;
7280
7281 store.assign_comm_ids();
7282 let second_id = store.messages.values().next().unwrap().comm_id;
7283
7284 assert_eq!(first_id, second_id, "assign_comm_ids should be idempotent");
7285 }
7286
7287 #[test]
7288 fn test_get_message_by_comm_id() {
7289 let mut store = CommStore::new();
7290 let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7291 let msg = store.send_message(ch_id, "alice", "hello", MessageType::Text).unwrap();
7292
7293 store.assign_comm_ids();
7294
7295 let comm_id = store.messages.get(&msg.id).unwrap().comm_id.unwrap();
7296 let found = store.get_message_by_comm_id(&comm_id);
7297 assert!(found.is_some());
7298 assert_eq!(found.unwrap().id, msg.id);
7299 }
7300
7301 #[test]
7302 fn test_get_channel_by_comm_id() {
7303 let mut store = CommStore::new();
7304 let ch = store.create_channel("test", ChannelType::Group, None).unwrap().clone();
7305
7306 store.assign_comm_ids();
7307
7308 let comm_id = store.channels.get(&ch.id).unwrap().comm_id.unwrap();
7309 let found = store.get_channel_by_comm_id(&comm_id);
7310 assert!(found.is_some());
7311 assert_eq!(found.unwrap().id, ch.id);
7312 }
7313
7314 #[test]
7315 fn test_send_rich_message() {
7316 let mut store = CommStore::new();
7317 let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7318 store.join_channel(ch_id, "alice").unwrap();
7319
7320 let content = MessageContent::Semantic(SemanticContent {
7321 text: "The weather is nice".into(),
7322 fragments: vec!["weather".into(), "nice".into()],
7323 context: Some("small talk".into()),
7324 perspective: None,
7325 });
7326
7327 let msg = store.send_rich_message(ch_id, "alice", content, MessageType::Text).unwrap();
7328 assert!(msg.rich_content_json.is_some());
7329 assert_eq!(msg.content, "The weather is nice");
7330
7331 let rich = store.get_rich_content(msg.id).unwrap();
7333 assert!(rich.is_some());
7334 let rc = rich.unwrap();
7335 assert!(rc.is_rich());
7336 assert_eq!(rc.as_text(), "The weather is nice");
7337 }
7338
7339 #[test]
7340 fn test_get_rich_content_none_for_plain_message() {
7341 let mut store = CommStore::new();
7342 let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7343 let msg = store.send_message(ch_id, "alice", "plain", MessageType::Text).unwrap();
7344
7345 let rich = store.get_rich_content(msg.id).unwrap();
7346 assert!(rich.is_none());
7347 }
7348
7349 #[test]
7350 fn test_get_rich_content_message_not_found() {
7351 let store = CommStore::new();
7352 let result = store.get_rich_content(9999);
7353 assert!(result.is_err());
7354 }
7355
7356 #[test]
7357 fn test_message_backward_compat_without_new_fields() {
7358 let json = r#"{
7360 "id": 1,
7361 "channel_id": 1,
7362 "sender": "alice",
7363 "recipient": null,
7364 "content": "hello",
7365 "message_type": "Text",
7366 "timestamp": "2026-01-01T00:00:00Z",
7367 "metadata": {},
7368 "signature": null,
7369 "acknowledged_by": [],
7370 "status": "Sent",
7371 "priority": "Normal",
7372 "reply_to": null,
7373 "correlation_id": null,
7374 "thread_id": null,
7375 "comm_timestamp": {"wall_clock": "2026-01-01T00:00:00Z", "lamport": 0, "agent_id": "alice", "vector_clock": {}}
7376 }"#;
7377 let msg: Message = serde_json::from_str(json).unwrap();
7378 assert!(msg.rich_content_json.is_none());
7379 assert!(msg.comm_id.is_none());
7380 assert_eq!(msg.content, "hello");
7381 }
7382
7383 #[test]
7388 fn test_trust_enforcement_send_message_blocked() {
7389 let mut store = CommStore::new();
7390 let ch = store
7391 .create_channel("secure", ChannelType::Group, None)
7392 .unwrap();
7393 let ch_id = ch.id;
7394
7395 let mut config = store.get_channel(ch_id).unwrap().config.clone();
7397 config.min_trust_level = Some(CommTrustLevel::High);
7398 store.set_channel_config(ch_id, config).unwrap();
7399
7400 store.set_trust_level("alice", CommTrustLevel::Basic).unwrap();
7402 store.join_channel(ch_id, "alice").unwrap_err(); store.set_trust_level("alice", CommTrustLevel::High).unwrap();
7406 store.join_channel(ch_id, "alice").unwrap();
7407
7408 store.set_trust_level("alice", CommTrustLevel::Basic).unwrap();
7410 let result = store.send_message(ch_id, "alice", "hello", MessageType::Text);
7411 assert!(result.is_err());
7412 let err = result.unwrap_err();
7413 assert!(
7414 err.to_string().contains("Trust level insufficient"),
7415 "Expected trust error, got: {}",
7416 err
7417 );
7418 }
7419
7420 #[test]
7421 fn test_trust_enforcement_send_message_allowed() {
7422 let mut store = CommStore::new();
7423 let ch = store
7424 .create_channel("secure", ChannelType::Group, None)
7425 .unwrap();
7426 let ch_id = ch.id;
7427
7428 let mut config = store.get_channel(ch_id).unwrap().config.clone();
7430 config.min_trust_level = Some(CommTrustLevel::Basic);
7431 store.set_channel_config(ch_id, config).unwrap();
7432
7433 store.set_trust_level("alice", CommTrustLevel::Standard).unwrap();
7435 store.join_channel(ch_id, "alice").unwrap();
7436 let msg = store.send_message(ch_id, "alice", "hello", MessageType::Text);
7437 assert!(msg.is_ok());
7438 }
7439
7440 #[test]
7441 fn test_trust_enforcement_no_min_trust_allows_all() {
7442 let mut store = CommStore::new();
7443 let ch = store
7444 .create_channel("open", ChannelType::Group, None)
7445 .unwrap();
7446 let ch_id = ch.id;
7447 store.set_trust_level("alice", CommTrustLevel::None).unwrap();
7449 store.join_channel(ch_id, "alice").unwrap();
7450 let msg = store.send_message(ch_id, "alice", "hello", MessageType::Text);
7451 assert!(msg.is_ok());
7452 }
7453
7454 #[test]
7455 fn test_trust_enforcement_broadcast_blocked() {
7456 let mut store = CommStore::new();
7457 let ch = store
7458 .create_channel("secure-bc", ChannelType::Broadcast, None)
7459 .unwrap();
7460 let ch_id = ch.id;
7461
7462 let mut config = store.get_channel(ch_id).unwrap().config.clone();
7464 config.min_trust_level = Some(CommTrustLevel::Full);
7465 store.set_channel_config(ch_id, config).unwrap();
7466
7467 store.set_trust_level("alice", CommTrustLevel::Standard).unwrap();
7469 let result = store.broadcast(ch_id, "alice", "hello everyone");
7470 assert!(result.is_err());
7471 assert!(result.unwrap_err().to_string().contains("Trust level insufficient"));
7472 }
7473
7474 #[test]
7475 fn test_trust_enforcement_join_channel_blocked() {
7476 let mut store = CommStore::new();
7477 let ch = store
7478 .create_channel("exclusive", ChannelType::Group, None)
7479 .unwrap();
7480 let ch_id = ch.id;
7481
7482 let mut config = store.get_channel(ch_id).unwrap().config.clone();
7484 config.min_trust_level = Some(CommTrustLevel::High);
7485 store.set_channel_config(ch_id, config).unwrap();
7486
7487 store.set_trust_level("bob", CommTrustLevel::Basic).unwrap();
7488 let result = store.join_channel(ch_id, "bob");
7489 assert!(result.is_err());
7490 assert!(result.unwrap_err().to_string().contains("Trust level insufficient"));
7491 }
7492
7493 #[test]
7494 fn test_trust_enforcement_publish_blocked() {
7495 let mut store = CommStore::new();
7496 let ch = store
7498 .create_channel("news-feed", ChannelType::PubSub, None)
7499 .unwrap();
7500 let ch_id = ch.id;
7501
7502 let mut config = store.get_channel(ch_id).unwrap().config.clone();
7503 config.min_trust_level = Some(CommTrustLevel::High);
7504 store.set_channel_config(ch_id, config).unwrap();
7505
7506 store.set_trust_level("low-trust-pub", CommTrustLevel::Minimal).unwrap();
7507
7508 store.subscribe("news-feed", "subscriber1").unwrap();
7510
7511 let result = store.publish("news-feed", "low-trust-pub", "breaking news");
7512 assert!(result.is_err());
7513 assert!(result.unwrap_err().to_string().contains("Trust level insufficient"));
7514 }
7515
7516 #[test]
7521 fn test_consent_join_channel_open_by_default() {
7522 let (mut store, ch_id) = new_store_with_channel();
7524 let result = store.join_channel(ch_id, "alice");
7525 assert!(result.is_ok());
7526 }
7527
7528 #[test]
7529 fn test_consent_join_channel_blocked() {
7530 let (mut store, ch_id) = new_store_with_channel();
7531 store.grant_consent(
7533 "system", "bob", ConsentScope::JoinChannels,
7534 Some("approved".to_string()), None,
7535 ).unwrap();
7536
7537 let result = store.join_channel(ch_id, "alice");
7539 assert!(result.is_err());
7540 assert!(result.unwrap_err().to_string().contains("Consent"));
7541
7542 let result = store.join_channel(ch_id, "bob");
7544 assert!(result.is_ok());
7545 }
7546
7547 #[test]
7548 fn test_consent_schedule_message_open_by_default() {
7549 let mut store = CommStore::new();
7550 let ch = store
7551 .create_channel("temporal-ch", ChannelType::Temporal, None)
7552 .unwrap();
7553 let result = store.schedule_message(
7555 ch.id, "alice", "future msg", TemporalTarget::Immediate, None,
7556 );
7557 assert!(result.is_ok());
7558 }
7559
7560 #[test]
7561 fn test_consent_schedule_message_blocked() {
7562 let mut store = CommStore::new();
7563 let ch = store
7564 .create_channel("temporal-ch", ChannelType::Temporal, None)
7565 .unwrap();
7566 store.grant_consent(
7568 "system", "bob", ConsentScope::ScheduleMessages,
7569 Some("allowed".to_string()), None,
7570 ).unwrap();
7571
7572 let result = store.schedule_message(
7573 ch.id, "alice", "blocked msg", TemporalTarget::Immediate, None,
7574 );
7575 assert!(result.is_err());
7576 assert!(result.unwrap_err().to_string().contains("Consent"));
7577 }
7578
7579 #[test]
7580 fn test_consent_form_hive_open_by_default() {
7581 let mut store = CommStore::new();
7582 let result = store.form_hive(
7583 "test-hive", "coordinator",
7584 CollectiveDecisionMode::Consensus,
7585 );
7586 assert!(result.is_ok());
7587 }
7588
7589 #[test]
7590 fn test_consent_form_hive_blocked() {
7591 let mut store = CommStore::new();
7592 store.grant_consent(
7594 "system", "other-agent", ConsentScope::HiveParticipation,
7595 Some("approved".to_string()), None,
7596 ).unwrap();
7597
7598 let result = store.form_hive(
7600 "test-hive", "coordinator",
7601 CollectiveDecisionMode::Consensus,
7602 );
7603 assert!(result.is_err());
7604 assert!(result.unwrap_err().to_string().contains("Consent"));
7605 }
7606
7607 #[test]
7608 fn test_consent_join_hive_blocked() {
7609 let mut store = CommStore::new();
7610 store.grant_consent(
7612 "system", "coordinator", ConsentScope::HiveParticipation,
7613 Some("approved".to_string()), None,
7614 ).unwrap();
7615 let hive = store.form_hive(
7616 "test-hive", "coordinator",
7617 CollectiveDecisionMode::Consensus,
7618 ).unwrap();
7619 let hive_id = hive.id;
7620
7621 let result = store.join_hive(hive_id, "joiner", HiveRole::Member);
7623 assert!(result.is_err());
7624 assert!(result.unwrap_err().to_string().contains("Consent"));
7625
7626 store.grant_consent(
7628 "system", "joiner", ConsentScope::HiveParticipation,
7629 Some("approved".to_string()), None,
7630 ).unwrap();
7631 let result = store.join_hive(hive_id, "joiner", HiveRole::Member);
7632 assert!(result.is_ok());
7633 }
7634
7635 #[test]
7636 fn test_consent_configure_federation_open_by_default() {
7637 let mut store = CommStore::new();
7638 let result = store.configure_federation(
7639 true, "zone-a", FederationPolicy::Allow,
7640 );
7641 assert!(result.is_ok());
7642 }
7643
7644 #[test]
7645 fn test_consent_configure_federation_blocked() {
7646 let mut store = CommStore::new();
7647 store.grant_consent(
7649 "admin", "other-system", ConsentScope::Federate,
7650 Some("allowed".to_string()), None,
7651 ).unwrap();
7652
7653 let result = store.configure_federation(
7655 true, "zone-a", FederationPolicy::Allow,
7656 );
7657 assert!(result.is_err());
7658 assert!(result.unwrap_err().to_string().contains("Consent"));
7659 }
7660
7661 #[test]
7666 fn test_vector_clock_increment() {
7667 let mut ts = CommTimestamp::now("agent-a");
7668 assert_eq!(ts.lamport, 0);
7669 assert_eq!(*ts.vector_clock.get("agent-a").unwrap(), 0);
7670
7671 ts.increment("agent-a");
7672 assert_eq!(ts.lamport, 1);
7673 assert_eq!(*ts.vector_clock.get("agent-a").unwrap(), 1);
7674
7675 ts.increment("agent-a");
7676 assert_eq!(ts.lamport, 2);
7677 assert_eq!(*ts.vector_clock.get("agent-a").unwrap(), 2);
7678 }
7679
7680 #[test]
7681 fn test_vector_clock_merge() {
7682 let mut ts_a = CommTimestamp::now("agent-a");
7683 ts_a.increment("agent-a"); ts_a.increment("agent-a"); let mut ts_b = CommTimestamp::now("agent-b");
7687 ts_b.increment("agent-b"); ts_a.merge(&ts_b, "agent-a");
7691 assert_eq!(ts_a.lamport, 3);
7693 assert_eq!(*ts_a.vector_clock.get("agent-a").unwrap(), 3);
7695 assert_eq!(*ts_a.vector_clock.get("agent-b").unwrap(), 1);
7696 }
7697
7698 #[test]
7699 fn test_vector_clock_happens_before() {
7700 let mut ts_a = CommTimestamp::now("agent-a");
7701 ts_a.increment("agent-a"); let mut ts_b = ts_a.clone();
7704 ts_b.increment("agent-a"); assert!(ts_a.happens_before(&ts_b));
7708 assert!(!ts_b.happens_before(&ts_a));
7710 }
7711
7712 #[test]
7713 fn test_vector_clock_concurrent() {
7714 let mut ts_a = CommTimestamp::now("agent-a");
7715 ts_a.increment("agent-a"); let mut ts_b = CommTimestamp::now("agent-b");
7718 ts_b.increment("agent-b"); assert!(!ts_a.happens_before(&ts_b));
7722 assert!(!ts_b.happens_before(&ts_a));
7723 }
7724
7725 #[test]
7726 fn test_vector_clock_populated_in_send_message() {
7727 let (mut store, ch_id) = new_store_with_channel();
7728 store.join_channel(ch_id, "alice").unwrap();
7729
7730 let msg = store
7731 .send_message(ch_id, "alice", "hello", MessageType::Text)
7732 .unwrap();
7733
7734 assert!(msg.comm_timestamp.lamport > 0);
7736 assert_eq!(
7737 *msg.comm_timestamp.vector_clock.get("alice").unwrap(),
7738 msg.comm_timestamp.lamport,
7739 );
7740 }
7741
7742 #[test]
7743 fn test_vector_clock_populated_in_broadcast() {
7744 let mut store = CommStore::new();
7745 let ch = store
7746 .create_channel("bc-chan", ChannelType::Broadcast, None)
7747 .unwrap();
7748 let ch_id = ch.id;
7749 store.join_channel(ch_id, "alice").unwrap();
7750 store.join_channel(ch_id, "bob").unwrap();
7751
7752 let msgs = store.broadcast(ch_id, "alice", "broadcast msg").unwrap();
7753 assert!(!msgs.is_empty());
7754 for m in &msgs {
7755 assert!(m.comm_timestamp.lamport > 0);
7756 assert_eq!(
7757 *m.comm_timestamp.vector_clock.get("alice").unwrap(),
7758 m.comm_timestamp.lamport,
7759 );
7760 }
7761 }
7762
7763 #[test]
7764 fn test_receive_messages_merges_lamport() {
7765 let (mut store, ch_id) = new_store_with_channel();
7766 store.join_channel(ch_id, "alice").unwrap();
7767 store.join_channel(ch_id, "bob").unwrap();
7768
7769 store.send_message(ch_id, "alice", "msg1", MessageType::Text).unwrap();
7771 store.send_message(ch_id, "alice", "msg2", MessageType::Text).unwrap();
7772 let lamport_after_send = store.lamport_counter;
7773
7774 let msgs = store.receive_messages(ch_id, None, None).unwrap();
7777 assert_eq!(msgs.len(), 2);
7778 assert!(store.lamport_counter >= lamport_after_send);
7780 }
7781
7782 #[test]
7783 fn test_channel_config_backward_compat_no_min_trust() {
7784 let json = r#"{
7786 "max_participants": 10,
7787 "ttl_seconds": 0,
7788 "persistence": true,
7789 "encryption_required": false
7790 }"#;
7791 let config: ChannelConfig = serde_json::from_str(json).unwrap();
7792 assert_eq!(config.min_trust_level, None);
7793 }
7794
7795 #[test]
7800 fn test_agent_registry_basic() {
7801 let mut store = CommStore::new();
7802 let agent = CommunicatingAgent {
7803 agent_id: "agent-1".to_string(),
7804 availability: Availability::Available,
7805 ..Default::default()
7806 };
7807 store.register_agent(agent).unwrap();
7808 assert!(store.get_agent("agent-1").is_some());
7809 assert_eq!(store.list_agents().len(), 1);
7810 }
7811
7812 #[test]
7813 fn test_agent_availability_update() {
7814 let mut store = CommStore::new();
7815 let agent = CommunicatingAgent {
7816 agent_id: "agent-1".to_string(),
7817 availability: Availability::Available,
7818 ..Default::default()
7819 };
7820 store.register_agent(agent).unwrap();
7821 store
7822 .update_agent_availability("agent-1", Availability::Busy)
7823 .unwrap();
7824 assert_eq!(
7825 store.get_agent("agent-1").unwrap().availability,
7826 Availability::Busy
7827 );
7828 }
7829
7830 #[test]
7831 fn test_agent_unregister() {
7832 let mut store = CommStore::new();
7833 let agent = CommunicatingAgent {
7834 agent_id: "agent-1".to_string(),
7835 ..Default::default()
7836 };
7837 store.register_agent(agent).unwrap();
7838 store.unregister_agent("agent-1").unwrap();
7839 assert!(store.get_agent("agent-1").is_none());
7840 assert!(store.unregister_agent("agent-1").is_err());
7841 }
7842
7843 #[test]
7844 fn test_bridge_config_set() {
7845 let mut store = CommStore::new();
7846 let config = BridgeConfig {
7847 identity_enabled: true,
7848 memory_enabled: true,
7849 ..Default::default()
7850 };
7851 store.set_bridge_config(config);
7852 assert!(store.bridge_config.identity_enabled);
7853 assert!(store.bridge_config.memory_enabled);
7854 assert!(!store.bridge_config.time_enabled);
7855 }
7856
7857 #[test]
7858 fn test_agent_update_nonexistent() {
7859 let mut store = CommStore::new();
7860 let result = store.update_agent_availability("ghost", Availability::Busy);
7861 assert!(result.is_err());
7862 }
7863
7864 #[test]
7865 fn test_agent_registry_audit_log() {
7866 let mut store = CommStore::new();
7867 let agent = CommunicatingAgent {
7868 agent_id: "agent-audit".to_string(),
7869 ..Default::default()
7870 };
7871 store.register_agent(agent).unwrap();
7872 store.unregister_agent("agent-audit").unwrap();
7873
7874 let register_entries: Vec<_> = store
7876 .audit_log
7877 .iter()
7878 .filter(|e| e.event_type == AuditEventType::AgentRegistered)
7879 .collect();
7880 let unregister_entries: Vec<_> = store
7881 .audit_log
7882 .iter()
7883 .filter(|e| e.event_type == AuditEventType::AgentUnregistered)
7884 .collect();
7885 assert_eq!(register_entries.len(), 1);
7886 assert_eq!(unregister_entries.len(), 1);
7887 }
7888
7889 #[test]
7890 fn test_agents_serde_backward_compat() {
7891 let store = CommStore::new();
7893 let json = serde_json::to_string(&store).unwrap();
7894 let deserialized: CommStore = serde_json::from_str(&json).unwrap();
7895 assert!(deserialized.agents.is_empty());
7896 }
7897}