pub mod conformance;
use std::sync::Mutex;
use std::time::Duration;
use jiff::Timestamp;
use okbot_core::{Clock, ConversationKey, MessageRole, NewMessage, UserId};
#[derive(Debug)]
pub struct ManualClock(Mutex<Timestamp>);
impl Default for ManualClock {
fn default() -> Self {
Self(Mutex::new(
"2026-01-01T00:00:00Z"
.parse()
.expect("valid timestamp literal"),
))
}
}
impl ManualClock {
pub fn advance(&self, by: Duration) {
let mut now = self
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*now = now
.checked_add(jiff::SignedDuration::try_from(by).expect("duration fits"))
.expect("timestamp in range");
}
}
impl Clock for ManualClock {
fn now(&self) -> Timestamp {
*self
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
pub fn conversation(channel: &str, room: &str) -> ConversationKey {
ConversationKey::room(channel.parse().expect("valid channel id"), room)
}
pub fn user_message(key: &ConversationKey, text: &str, at: Timestamp) -> NewMessage {
NewMessage {
conversation: key.clone(),
role: MessageRole::User,
author_id: Some(UserId::new("u1")),
author_name: "Ana".into(),
text: text.into(),
message_id: None,
at,
}
}
pub fn bot_message(key: &ConversationKey, text: &str, at: Timestamp) -> NewMessage {
NewMessage {
conversation: key.clone(),
role: MessageRole::Bot,
author_id: None,
author_name: "okbot".into(),
text: text.into(),
message_id: None,
at,
}
}
mod fakes;
mod memory_store;
mod memory_tasks;
pub use fakes::{CollectSink, FakeAgent, FakeChannel, PlainRenderer, fake_cli, inbound, turn};
pub use memory_store::MemoryStore;
pub use memory_tasks::MemoryTasks;