use std::fmt;
use std::str::FromStr;
use foundation_compact::ids::{Id, ParseError};
use foundation_compact::SystemTime;
use serde::{Deserialize, Serialize};
use super::base_types::Messages;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SessionId(Id);
impl SessionId {
#[must_use]
pub fn new() -> Self {
Self(foundation_compact::ids::new_scru128_with_machine_id(
machine_id(),
))
}
#[must_use]
pub fn from_name(name: &str) -> Self {
Self(foundation_compact::ids::scru128_from_name_with_machine_id(
name,
machine_id(),
))
}
#[must_use]
pub fn timestamp_ms(&self) -> u64 {
self.0.timestamp()
}
#[must_use]
pub fn machine_id(&self) -> u32 {
foundation_compact::ids::machine_id_of(&self.0)
}
#[must_use]
pub fn id(&self) -> &Id {
&self.0
}
}
impl Default for SessionId {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for SessionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for SessionId {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Id::from_str(s).map(SessionId)
}
}
fn machine_id() -> u32 {
use std::sync::OnceLock;
static MACHINE_ID: OnceLock<u32> = OnceLock::new();
*MACHINE_ID.get_or_init(|| {
let host = std::env::var("HOSTNAME")
.or_else(|_| std::env::var("COMPUTERNAME"))
.ok();
match host {
Some(h) if !h.is_empty() => {
#[allow(clippy::cast_possible_truncation)]
let mid = foundation_compact::ids::stable_hash(h.as_bytes()) as u32;
mid
}
_ => foundation_compact::entropy::u32().unwrap_or(0),
}
})
}
pub use crate::agentic::errors::AgenticError;
pub use crate::agentic::token_ledger::TokenSnapshot;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "message_type", rename_all = "snake_case")]
pub enum SessionRecord {
Conversation { message: Messages },
Retracted {
#[serde(default = "fresh_record_id")]
id: Id,
reason: String,
timestamp: SystemTime,
},
WorkingMemory {
#[serde(default = "fresh_record_id")]
id: Id,
facts: Vec<MemoryFact>,
version: u64,
timestamp: SystemTime,
},
Observation {
#[serde(default = "fresh_record_id")]
id: Id,
observations: Vec<ObservationEntry>,
token_count: u64,
timestamp: SystemTime,
},
Reflection {
#[serde(default = "fresh_record_id")]
id: Id,
reflections: Vec<ReflectionEntry>,
generated_at: SystemTime,
observation_token_count_before: u64,
reflection_token_count_after: u64,
},
FailedAction {
error: AgenticError,
trace: foundation_errstacks::StructuredErrorTrace,
},
Summary {
message_count: u64,
usage: TokenSnapshot,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MemoryFact {
pub fact: String,
pub asserted_at: SystemTime,
pub source_message_id: Id,
pub confidence: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ObservationEntry {
#[serde(rename = "type")]
pub kind: ObservationKind,
pub content: String,
pub timestamp: SystemTime,
pub source_message_id: Id,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ObservationKind {
Assertion,
Question,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReflectionEntry {
pub summary: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_range: Option<TimeRange>,
pub observation_refs: Vec<Id>,
pub importance: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimeRange {
pub from: SystemTime,
pub to: SystemTime,
}
fn fresh_record_id() -> Id {
foundation_compact::ids::new_scru128()
}