use crate::core::types::ConversationMessage; /// The in-memory conversation history for the current session. /// /// Stores messages as a flat ordered list. Each [`push`][`Self::push`] appends /// one message; [`messages`][`Self::messages`] returns a slice over all of them. /// /// This is a flat list for Phase 1. Phases 3+ will introduce a tree structure /// (each event carrying a `parent_id`) to support conversation branching and /// sub-agent threads. The flat model is upward-compatible: a tree is just a /// linear chain of parent IDs when there is no branching. pub struct ConversationHistory { messages: Vec, } impl ConversationHistory { /// Create an empty history. pub fn new() -> Self { Self { messages: Vec::new(), } } /// Append one message to the end of the history. pub fn push(&mut self, message: ConversationMessage) { self.messages.push(message); } /// Return the full ordered message list, oldest-first. /// /// This slice is what gets serialised and sent to the provider on each /// turn -- the provider needs the full prior context to generate a coherent /// continuation. pub fn messages(&self) -> &[ConversationMessage] { &self.messages } /// Remove all messages from the history. pub fn clear(&mut self) { self.messages.clear(); } } impl Default for ConversationHistory { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use crate::core::types::Role; #[test] fn new_history_is_empty() { let history = ConversationHistory::new(); assert!(history.messages().is_empty()); } #[test] fn push_and_read_roundtrip() { let mut history = ConversationHistory::new(); history.push(ConversationMessage { role: Role::User, content: "hello".to_string(), }); history.push(ConversationMessage { role: Role::Assistant, content: "hi there".to_string(), }); let msgs = history.messages(); assert_eq!(msgs.len(), 2); assert_eq!(msgs[0].role, Role::User); assert_eq!(msgs[0].content, "hello"); assert_eq!(msgs[1].role, Role::Assistant); assert_eq!(msgs[1].content, "hi there"); } #[test] fn clear_empties_history() { let mut history = ConversationHistory::new(); history.push(ConversationMessage { role: Role::User, content: "hello".to_string(), }); history.clear(); assert!(history.messages().is_empty()); } #[test] fn messages_preserves_insertion_order() { let mut history = ConversationHistory::new(); for i in 0u32..5 { history.push(ConversationMessage { role: Role::User, content: format!("msg {i}"), }); } for (i, msg) in history.messages().iter().enumerate() { assert_eq!(msg.content, format!("msg {i}")); } } }