Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ minidom = "0.15"
jid = "0.10"

# TLS
rustls = "0.23"
rustls = { version = "0.23", features = ["ring"] }
tokio-rustls = "0.26"
rustls-pemfile = "2"

Expand Down
5 changes: 4 additions & 1 deletion chattermax-core/src/sm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ impl Enabled {

let max = elem
.attr("max")
.map(|s| s.parse::<u32>().map_err(|_| "Invalid 'max' value".to_string()))
.map(|s| {
s.parse::<u32>()
.map_err(|_| "Invalid 'max' value".to_string())
})
.transpose()?;

Ok(Self { id, resume, max })
Expand Down
28 changes: 20 additions & 8 deletions chattermax-core/src/types/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,10 @@ mod tests {
reason: FreezeReason::TaskComplete,
conversation_context: ConversationContext {
room_jid: Some("room@example.com".to_string()),
participants: vec!["user1@example.com".to_string(), "user2@example.com".to_string()],
participants: vec![
"user1@example.com".to_string(),
"user2@example.com".to_string(),
],
last_message_id: Some("msg-123".to_string()),
},
active_context_ref: Some("ctx-ref-1".to_string()),
Expand All @@ -433,7 +436,8 @@ mod tests {
};

let json = serde_json::to_string(&freeze_notif).expect("serialization failed");
let deserialized: FreezeNotification = serde_json::from_str(&json).expect("deserialization failed");
let deserialized: FreezeNotification =
serde_json::from_str(&json).expect("deserialization failed");

assert_eq!(deserialized.agent_jid, freeze_notif.agent_jid);
assert_eq!(deserialized.frozen_at, freeze_notif.frozen_at);
Expand All @@ -443,23 +447,26 @@ mod tests {
fn test_freeze_reason_task_complete() {
let reason = FreezeReason::TaskComplete;
let json = serde_json::to_string(&reason).expect("serialization failed");
let deserialized: FreezeReason = serde_json::from_str(&json).expect("deserialization failed");
let deserialized: FreezeReason =
serde_json::from_str(&json).expect("deserialization failed");
assert_eq!(deserialized, FreezeReason::TaskComplete);
}

#[test]
fn test_freeze_reason_user_requested() {
let reason = FreezeReason::UserRequested;
let json = serde_json::to_string(&reason).expect("serialization failed");
let deserialized: FreezeReason = serde_json::from_str(&json).expect("deserialization failed");
let deserialized: FreezeReason =
serde_json::from_str(&json).expect("deserialization failed");
assert_eq!(deserialized, FreezeReason::UserRequested);
}

#[test]
fn test_freeze_reason_error() {
let reason = FreezeReason::Error("Something went wrong".to_string());
let json = serde_json::to_string(&reason).expect("serialization failed");
let deserialized: FreezeReason = serde_json::from_str(&json).expect("deserialization failed");
let deserialized: FreezeReason =
serde_json::from_str(&json).expect("deserialization failed");
match deserialized {
FreezeReason::Error(msg) => assert_eq!(msg, "Something went wrong"),
_ => panic!("Expected Error variant"),
Expand All @@ -470,7 +477,8 @@ mod tests {
fn test_freeze_reason_timeout() {
let reason = FreezeReason::Timeout;
let json = serde_json::to_string(&reason).expect("serialization failed");
let deserialized: FreezeReason = serde_json::from_str(&json).expect("deserialization failed");
let deserialized: FreezeReason =
serde_json::from_str(&json).expect("deserialization failed");
assert_eq!(deserialized, FreezeReason::Timeout);
}

Expand All @@ -483,7 +491,8 @@ mod tests {
};

let json = serde_json::to_string(&context).expect("serialization failed");
let deserialized: ConversationContext = serde_json::from_str(&json).expect("deserialization failed");
let deserialized: ConversationContext =
serde_json::from_str(&json).expect("deserialization failed");

assert_eq!(deserialized.room_jid, context.room_jid);
assert_eq!(deserialized.participants.len(), 1);
Expand Down Expand Up @@ -522,7 +531,10 @@ mod tests {

#[test]
fn test_freeze_notification_as_str() {
assert_eq!(MessageType::FreezeNotification.as_str(), "freeze_notification");
assert_eq!(
MessageType::FreezeNotification.as_str(),
"freeze_notification"
);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion chattermax-core/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ pub mod serialization;
pub use context_ref::{ContextRef, ContextRefParseError};
pub use message::{
Answer, CodeChange, FeatureComplete, Integration, Message, MessageType, Metadata, Question,
ReviewComment, StatusUpdate, Thought, ThawRequest, Todo, ToolCall, ToolResult, WorkAvailable,
ReviewComment, StatusUpdate, ThawRequest, Thought, Todo, ToolCall, ToolResult, WorkAvailable,
};
pub use serialization::{from_xml, to_xml};
82 changes: 56 additions & 26 deletions chattermax-core/src/types/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,25 +799,33 @@ fn deserialize_feature_complete(element: &Element) -> Result<Message> {
}

fn serialize_freeze_notification(freeze_notification: &FreezeNotification) -> Result<Element> {
let mut elem = Element::builder("freeze_notification", "urn:chattermax:xep:freeze-notification:0")
.append(
Element::builder("agent_jid", "urn:chattermax:xep:freeze-notification:0")
.append(freeze_notification.agent_jid.clone())
.build(),
)
.append(serialize_freeze_reason(&freeze_notification.reason))
.append(serialize_conversation_context(&freeze_notification.conversation_context))
.append(
Element::builder("frozen_at", "urn:chattermax:xep:freeze-notification:0")
.append(freeze_notification.frozen_at.clone())
.build(),
);
let mut elem = Element::builder(
"freeze_notification",
"urn:chattermax:xep:freeze-notification:0",
)
.append(
Element::builder("agent_jid", "urn:chattermax:xep:freeze-notification:0")
.append(freeze_notification.agent_jid.clone())
.build(),
)
.append(serialize_freeze_reason(&freeze_notification.reason))
.append(serialize_conversation_context(
&freeze_notification.conversation_context,
))
.append(
Element::builder("frozen_at", "urn:chattermax:xep:freeze-notification:0")
.append(freeze_notification.frozen_at.clone())
.build(),
);

if let Some(active_context_ref) = &freeze_notification.active_context_ref {
elem = elem.append(
Element::builder("active_context_ref", "urn:chattermax:xep:freeze-notification:0")
.append(active_context_ref.clone())
.build(),
Element::builder(
"active_context_ref",
"urn:chattermax:xep:freeze-notification:0",
)
.append(active_context_ref.clone())
.build(),
);
}

Expand Down Expand Up @@ -875,7 +883,10 @@ fn serialize_freeze_reason(reason: &FreezeReason) -> Element {
}

fn serialize_conversation_context(context: &ConversationContext) -> Element {
let mut elem = Element::builder("conversation_context", "urn:chattermax:xep:freeze-notification:0");
let mut elem = Element::builder(
"conversation_context",
"urn:chattermax:xep:freeze-notification:0",
);

if let Some(room_jid) = &context.room_jid {
elem = elem.append(
Expand All @@ -885,7 +896,8 @@ fn serialize_conversation_context(context: &ConversationContext) -> Element {
);
}

let mut participants_elem = Element::builder("participants", "urn:chattermax:xep:freeze-notification:0");
let mut participants_elem =
Element::builder("participants", "urn:chattermax:xep:freeze-notification:0");
for participant in &context.participants {
participants_elem = participants_elem.append(
Element::builder("participant", "urn:chattermax:xep:freeze-notification:0")
Expand All @@ -897,9 +909,12 @@ fn serialize_conversation_context(context: &ConversationContext) -> Element {

if let Some(last_message_id) = &context.last_message_id {
elem = elem.append(
Element::builder("last_message_id", "urn:chattermax:xep:freeze-notification:0")
.append(last_message_id.clone())
.build(),
Element::builder(
"last_message_id",
"urn:chattermax:xep:freeze-notification:0",
)
.append(last_message_id.clone())
.build(),
);
}

Expand All @@ -918,7 +933,10 @@ fn deserialize_freeze_notification(element: &Element) -> Result<Message> {
.and_then(deserialize_freeze_reason)?;

let conversation_context = element
.get_child("conversation_context", "urn:chattermax:xep:freeze-notification:0")
.get_child(
"conversation_context",
"urn:chattermax:xep:freeze-notification:0",
)
.ok_or_else(|| {
Error::ParseError("Missing conversation_context in freeze_notification".to_string())
})
Expand All @@ -930,7 +948,10 @@ fn deserialize_freeze_notification(element: &Element) -> Result<Message> {
.ok_or_else(|| Error::ParseError("Missing frozen_at in freeze_notification".to_string()))?;

let active_context_ref = element
.get_child("active_context_ref", "urn:chattermax:xep:freeze-notification:0")
.get_child(
"active_context_ref",
"urn:chattermax:xep:freeze-notification:0",
)
.map(|e| e.text());

let metadata = extract_metadata(element)?;
Expand Down Expand Up @@ -989,7 +1010,10 @@ fn deserialize_conversation_context(element: &Element) -> Result<ConversationCon
}

let last_message_id = element
.get_child("last_message_id", "urn:chattermax:xep:freeze-notification:0")
.get_child(
"last_message_id",
"urn:chattermax:xep:freeze-notification:0",
)
.map(|e| e.text());

Ok(ConversationContext {
Expand Down Expand Up @@ -1405,8 +1429,14 @@ mod tests {

match deserialized {
Message::ThawRequest(tr) => {
assert_eq!(tr.resurrection_room_jid, Some("room2@conference.chattermax.local".to_string()));
assert_eq!(tr.additional_context, Some("Resume context from checkpoint 5".to_string()));
assert_eq!(
tr.resurrection_room_jid,
Some("room2@conference.chattermax.local".to_string())
);
assert_eq!(
tr.additional_context,
Some("Resume context from checkpoint 5".to_string())
);
}
_ => panic!("Wrong message type"),
}
Expand Down
6 changes: 3 additions & 3 deletions chattermax-server/src/context_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! This module wraps the core ContextResolver with server-specific configuration,
//! including base URL loading from environment and cache settings.

use chattermax_core::chizu::{ChizuClient, ContextResolver, ChizuError, KnowledgePack};
use chattermax_core::chizu::{ChizuClient, ChizuError, ContextResolver, KnowledgePack};
use chattermax_core::types::ContextRef;
use std::time::Duration;
use tracing::{debug, warn};
Expand All @@ -28,8 +28,8 @@ impl ServerContextResolver {
/// let resolver = ServerContextResolver::new();
/// ```
pub fn new() -> Self {
let base_url = std::env::var("CHIZU_BASE_URL")
.unwrap_or_else(|_| "http://localhost:8080".to_string());
let base_url =
std::env::var("CHIZU_BASE_URL").unwrap_or_else(|_| "http://localhost:8080".to_string());

debug!("Creating ServerContextResolver with base URL: {}", base_url);

Expand Down
31 changes: 23 additions & 8 deletions chattermax-server/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,8 @@ impl Database {
expires_seconds: u32,
) -> Result<()> {
let expires_at = Utc::now() + chrono::Duration::seconds(expires_seconds as i64);
let stanzas_json = serde_json::to_string(unacked_stanzas).unwrap_or_else(|_| "[]".to_string());
let stanzas_json =
serde_json::to_string(unacked_stanzas).unwrap_or_else(|_| "[]".to_string());

sqlx::query(
r#"
Expand Down Expand Up @@ -621,8 +622,11 @@ impl Database {
let created_str: String = row.get("created_at");
let expires_str: String = row.get("expires_at");

let jid: Jid = jid_str.parse().map_err(|e| anyhow::anyhow!("Invalid JID: {}", e))?;
let unacked_stanzas: Vec<String> = serde_json::from_str(&stanzas_json).unwrap_or_default();
let jid: Jid = jid_str
.parse()
.map_err(|e| anyhow::anyhow!("Invalid JID: {}", e))?;
let unacked_stanzas: Vec<String> =
serde_json::from_str(&stanzas_json).unwrap_or_default();

// SQLite datetime format is "YYYY-MM-DD HH:MM:SS", convert to RFC3339
let created_at = parse_sqlite_datetime(&created_str)?;
Expand Down Expand Up @@ -722,9 +726,14 @@ mod tests {
let (_dir, db) = setup_test_db().await;
let jid: Jid = "user@example.com/resource".parse().unwrap();
let token = "test-token-12345";
let unacked = vec!["<message id='1'/>".to_string(), "<message id='2'/>".to_string()];
let unacked = vec![
"<message id='1'/>".to_string(),
"<message id='2'/>".to_string(),
];

db.store_stream_session(token, &jid, 5, 10, &unacked, 300).await.unwrap();
db.store_stream_session(token, &jid, 5, 10, &unacked, 300)
.await
.unwrap();

let session = db.get_stream_session(token).await.unwrap();
assert!(session.is_some());
Expand All @@ -749,7 +758,9 @@ mod tests {
let jid: Jid = "user@example.com/resource".parse().unwrap();
let token = "test-token-delete";

db.store_stream_session(token, &jid, 0, 0, &[], 300).await.unwrap();
db.store_stream_session(token, &jid, 0, 0, &[], 300)
.await
.unwrap();
assert!(db.get_stream_session(token).await.unwrap().is_some());

db.delete_stream_session(token).await.unwrap();
Expand All @@ -762,12 +773,16 @@ mod tests {
let jid: Jid = "user@example.com/resource".parse().unwrap();
let token = "test-token-upsert";

db.store_stream_session(token, &jid, 0, 0, &[], 300).await.unwrap();
db.store_stream_session(token, &jid, 0, 0, &[], 300)
.await
.unwrap();
let session = db.get_stream_session(token).await.unwrap().unwrap();
assert_eq!(session.last_handled_inbound, 0);

// Update with new values
db.store_stream_session(token, &jid, 10, 20, &["<msg/>".to_string()], 300).await.unwrap();
db.store_stream_session(token, &jid, 10, 20, &["<msg/>".to_string()], 300)
.await
.unwrap();
let session = db.get_stream_session(token).await.unwrap().unwrap();
assert_eq!(session.last_handled_inbound, 10);
assert_eq!(session.last_handled_outbound, 20);
Expand Down
5 changes: 4 additions & 1 deletion chattermax-server/src/freeze/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ impl FreezeHandler {
/// # Returns
/// Vector containing references to all FrozenAgentState entries
pub fn list_frozen_agents(&self) -> Vec<&FrozenAgentState> {
debug!("Listing all frozen agents (count: {})", self.frozen_agents.len());
debug!(
"Listing all frozen agents (count: {})",
self.frozen_agents.len()
);
self.frozen_agents.values().collect()
}
}
Expand Down
Loading
Loading