Skip to main content

ijima_core/
session.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Session-context repository domain types — the raw session transcript
5//! store (HANDOFF §4). Append-only, high-fidelity; mined into the memory
6//! palace by `ijima-miner`.
7
8use crate::harness::Harness;
9
10/// Stable opaque identifier for a session.
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "serde", serde(transparent))]
14pub struct SessionId(pub String);
15
16impl SessionId {
17    /// Construct a session id.
18    pub fn new(id: impl Into<String>) -> Self {
19        Self(id.into())
20    }
21
22    /// The stable wire string.
23    pub fn as_str(&self) -> &str {
24        &self.0
25    }
26}
27
28/// The role of a session turn's author.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub enum TurnRole {
32    /// The user / operator.
33    User,
34    /// The agent / assistant.
35    Assistant,
36    /// A system message.
37    System,
38    /// A tool-call result.
39    Tool,
40}
41
42/// A single raw turn in a session transcript.
43///
44/// Provenance — which harness produced this turn — is carried by the
45/// enclosing session, not duplicated per turn.
46#[derive(Debug, Clone, PartialEq, Eq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct SessionTurn {
49    /// The session this turn belongs to.
50    pub session_id: SessionId,
51    /// 0-based index of this turn within the session.
52    pub turn_index: u64,
53    /// Author role.
54    pub role: TurnRole,
55    /// Raw turn content.
56    pub content: String,
57    /// ISO-8601 timestamp (matches pi-mempalace's TEXT timestamps).
58    pub timestamp: String,
59}
60
61/// A session descriptor — the metadata for a raw transcript.
62#[derive(Debug, Clone, PartialEq, Eq)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64pub struct Session {
65    /// Stable opaque identifier.
66    pub id: SessionId,
67    /// Which harness produced this session.
68    pub harness: Harness,
69    /// Gateway/channel/thread identifier (e.g. a Discord thread id).
70    pub channel: Option<String>,
71    /// ISO-8601 start timestamp.
72    pub started_at: String,
73    /// ISO-8601 end timestamp, if the session has ended.
74    pub ended_at: Option<String>,
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn session_turn_round_trips() {
83        let t = SessionTurn {
84            session_id: SessionId::new("sess_1"),
85            turn_index: 0,
86            role: TurnRole::User,
87            content: "hello".into(),
88            timestamp: "2026-07-05T12:00:00Z".into(),
89        };
90        assert_eq!(t.session_id.as_str(), "sess_1");
91        assert_eq!(t.role, TurnRole::User);
92        assert_eq!(t.turn_index, 0);
93    }
94}