docs: tambah doc comment, logging, dan inline comments di semua 255 file

Meliputi:
- File-level //! doc comment: tujuan file, alur kerja, komponen utama
- Function-level /// doc comment: apa, parameter, return, flow, edge cases
- Struct/enum/trait /// doc comment: peran, field docs
- Tracing logging (tracing::info!/debug!/trace!/warn!/error!) di setiap fungsi
- Inline comments untuk variable dan branching logic penting
- Seluruh 8 crates di workspace: zesdex-backend, zesdex-cms, zesdex-entities,
  zesdex-iam, zesdex-infra, zesdex-ipc, zesdex-middleware, zesdex-utils
- Build: 0 errors, 242/242 tests passed
This commit is contained in:
asepharyana
2026-07-19 17:05:47 +07:00
parent 6680795ce7
commit 5aaedbf787
255 changed files with 5666 additions and 743 deletions
@@ -1,4 +1,9 @@
//! Authentication entities: session metadata and PID-file lock.
//!
//! # Types
//!
//! - [`Session`](session::Session) — Authenticated user session with tokens, expiry, refresh
//! - [`SessionLock`](session_lock::SessionLock) — Exclusive PID-based lock to prevent concurrent sessions
pub mod session;
pub mod session_lock;
@@ -1,22 +1,45 @@
//! Session metadata: id, title, workspace roots, and message/token counts,
//! persisted as `session.json` per session directory.
//!
//! # Flow
//!
//! Created via [`Session::new`] → mutated in-memory → persisted via [`Session::save`]
//! (atomic write with fsync). Loaded back via [`Session::load`] or enumerated via
//! [`Session::list`]. Directory traversal is blocked by input validation in `load`.
//!
//! # Components
//!
//! - `Session` struct — fields for all session metadata
//! - `new` — timestamped constructor
//! - `save` / `load` / `list` — CRUD against the filesystem
use chrono::Utc;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tracing;
/// Metadata for one conversation session (distinct from the message
/// history itself, which lives in `Conversation`/the msglog).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
/// Unique session identifier (validated against path traversal in `load`).
pub id: String,
/// Epoch-millis timestamp of creation (`Utc::now().timestamp_millis()`).
pub created_at: i64,
/// Epoch-millis timestamp of last update.
pub updated_at: i64,
/// Human-readable title for the conversation.
pub title: String,
/// Model identifier string, e.g. `"anthropic/claude-opus-4-8"`.
pub model: String,
/// Workspace root directories associated with this session.
pub workspace_roots: Vec<PathBuf>,
/// Running count of messages in the conversation.
pub message_count: u32,
/// Running count of tokens consumed.
pub token_count: u32,
/// Soft-delete flag — archived sessions are hidden from the default list.
pub archived: bool,
/// Optional AI-generated conversation summary (used for compact context).
pub summary: Option<String>,
}
@@ -64,6 +87,7 @@ impl Session {
let dir = self.session_dir(base_dir);
std::fs::create_dir_all(&dir)?;
let path = dir.join("session.json");
tracing::debug!(id = %self.id, path = %path.display(), "saving session metadata");
zesdex_utils::write_json_atomic(&path, self, None)?;
Ok(())
}
@@ -85,7 +109,8 @@ impl Session {
));
}
let path = base_dir.join("sessions").join(id).join("session.json");
let data = std::fs::read_to_string(path)?;
tracing::debug!(id = %id, path = %path.display(), "loading session metadata");
let data = std::fs::read_to_string(&path)?;
let session: Session = serde_json::from_str(&data)?;
Ok(session)
}
@@ -101,6 +126,7 @@ impl Session {
pub fn list(base_dir: &Path) -> Vec<Self> {
let sessions_dir = base_dir.join("sessions");
let Ok(entries) = std::fs::read_dir(&sessions_dir) else {
tracing::warn!(path = %sessions_dir.display(), "sessions directory not found");
return Vec::new();
};
entries
@@ -1,14 +1,32 @@
//! PID-file based advisory lock preventing two processes from operating on
//! the same session directory concurrently.
//!
//! # Flow
//!
//! [`SessionLock::new`] creates a handle → [`SessionLock::try_lock`] attempts
//! atomic `O_CREAT|O_EXCL` creation. If the lock file already exists, the
//! owning PID is checked via `kill(pid, 0)` + `/proc/<pid>/exe` verification.
//! Stale locks are overwritten atomically (temp-file + rename + fsync).
//! On [`Drop`], the lock file is removed automatically.
//!
//! # Components
//!
//! - `SessionLock` — RAII guard wrapping a lock file path and PID
//! - `try_lock` — three-phase atomic acquire with stale-lock recovery
//! - `unlock` / `Drop` — explicit and implicit release
//! - `is_alive` — liveness check via `libc::kill` + `/proc` verification
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use tracing;
/// A PID-file lock (`<session_dir>/.lock`) tied to the current process,
/// auto-removed on drop.
#[derive(Debug)]
pub struct SessionLock {
/// Path to the `.lock` file inside the session directory.
path: PathBuf,
/// Process ID that holds (or will hold) this lock.
pid: u32,
}
@@ -47,9 +65,11 @@ impl SessionLock {
Ok(mut file) => {
write!(file, "{}", self.pid)?;
file.sync_all()?;
tracing::debug!(path = %self.path.display(), pid = self.pid, "session lock acquired");
return Ok(true);
}
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
tracing::debug!(path = %self.path.display(), "session lock already exists, checking staleness");
// Lock file exists — check if it's stale.
}
Err(e) => return Err(e),
@@ -59,8 +79,10 @@ impl SessionLock {
let content = fs::read_to_string(&self.path).unwrap_or_default();
if let Ok(pid) = content.trim().parse::<u32>() {
if Self::is_alive(pid) {
tracing::warn!(stale = pid, path = %self.path.display(), "session lock held by live process");
return Ok(false);
}
tracing::debug!(stale = pid, "stale lock detected, overwriting");
}
// Phase 3: stale lock — overwrite it atomically (best-effort).
@@ -1,17 +1,39 @@
//! In-memory conversation state: message history plus the system prompt and
//! model parameters used to drive the LLM.
//!
//! # Flow
//!
//! [`Conversation::new`] → [`push`](Conversation::push) to add messages →
//! [`to_api_messages`](Conversation::to_api_messages) to format for the LLM
//! API (system prompt prepended). Persisted via [`save_conversation`](Conversation::save_conversation)
//! and loaded via [`load_conversation`](Conversation::load_conversation).
//! The system prompt can be hot-swapped via [`rebuild_system`](Conversation::rebuild_system).
//!
//! # Components
//!
//! - `Conversation` — message vector + session metadata + generation params
//! - `push` / `rebuild_system` — mutation helpers
//! - `to_api_messages` — formats messages for API consumption
//! - `save_conversation` / `load_conversation` — filesystem persistence
use serde::{Deserialize, Serialize};
use tracing;
use super::message::{ChatMessage, Role};
/// A single conversation's message history and generation settings.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Conversation {
/// Ordered list of chat messages (user, assistant, tool, system).
pub messages: Vec<ChatMessage>,
/// System prompt prepended at request time (see `to_api_messages`).
pub system_prompt: String,
/// Foreign key referencing the owning session.
pub session_id: String,
/// Model identifier string, e.g. `"anthropic/claude-opus-4-8"`.
pub model: String,
/// Optional cap on output tokens.
pub max_tokens: Option<u32>,
/// Optional temperature (0.0 2.0).
pub temperature: Option<f32>,
}
@@ -78,6 +100,7 @@ impl Conversation {
let dir = base_dir.join("sessions").join(&self.session_id);
std::fs::create_dir_all(&dir)?;
let path = dir.join("conversation.json");
tracing::debug!(session_id = %self.session_id, path = %path.display(), "saving conversation");
zesdex_utils::write_json_atomic(&path, self, None)?;
Ok(())
}
@@ -97,7 +120,8 @@ impl Conversation {
.join("sessions")
.join(session_id)
.join("conversation.json");
let data = std::fs::read_to_string(path)?;
tracing::debug!(session_id = %session_id, path = %path.display(), "loading conversation");
let data = std::fs::read_to_string(&path)?;
let conv: Conversation = serde_json::from_str(&data)?;
Ok(conv)
}
@@ -1,8 +1,20 @@
//! Chat message types shared across the entity layer: `Role` and `ChatMessage`
//! with convenience constructors.
//! Chat message types shared across the entity layer.
//!
//! Provides [`Role`] (conversation participant) and [`ChatMessage`] (a single
//! message with optional tool-call metadata). Includes convenience constructors
//! for each role: `user`, `assistant`, `system`, `tool`/`tool_result`.
//!
//! # Flow
//!
//! Messages are constructed via the typed constructors → pushed into
//! [`Conversation`](super::conversation::Conversation) → serialized as JSON
//! to `conversation.json`.
use serde::{Deserialize, Serialize};
/// The conversation participant who authored a message.
///
/// Variants: `User`, `Assistant`, `System`, `Tool`. Serialized as lowercase
/// strings (e.g. `"user"`, `"assistant"`, `"system"`, `"tool"`).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Role {
#[serde(rename = "user")]
@@ -37,12 +49,18 @@ impl std::fmt::Display for Role {
/// chat-completion API structures.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
/// Who sent this message (user, assistant, system, tool).
pub role: Role,
/// The message text content. `None` for assistant messages that only
/// contain tool calls.
pub content: Option<String>,
/// Tool-call requests attached to an assistant message (OpenAI-style).
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<super::tool_call::ToolCall>>,
/// For tool-role messages: the `id` of the `ToolCall` being responded to.
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
/// Optional function name for the tool invocation.
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
@@ -1,6 +1,20 @@
//! Common entity types shared across the Zesdex application: application
//! configuration, settings, store paths, conversations, messages, tool
//! calls, usage stats, and SSE streaming types.
//! Common entity types shared across the Zesdex application.
//!
//! Contains pure data structures for conversations, messages, tool calls,
//! usage statistics, provider API types (chat request/response, SSE stream),
//! and store path configuration. All types derive `Serialize`/`Deserialize`
//! and are persisted as JSON files.
//!
//! # Sub-modules
//!
//! - [`conversation`] — Ordered conversation (vector of `ChatMessage`)
//! - [`message`] — `ChatMessage` + `Role` enum
//! - [`provider`] — LLM provider API types: `ChatRequest`, `ChatResponse`,
//! `StreamEvent`, `SseParser`, `ToolDef`, etc.
//! - [`store`] — `Store` paths for data directories
//! - [`tool_call`] — `ToolCall` + `ToolFunction` (function-calling request)
//! - [`tool_result`] — `ToolCallResult` (function-calling response)
//! - [`usage`] — `UsageStats` (token counts, costs)
pub mod conversation;
pub mod message;
@@ -1,7 +1,25 @@
//! Provider-facing DTOs: chat completion request, response, streaming types,
//! and the SSE stream parser.
//!
//! # Flow
//!
//! 1. **Request** — [`ChatRequest`] is built with model, messages, tools,
//! streaming options and sent to the LLM provider.
//! 2. **Response** — Non-streaming responses arrive as [`ChatResponse`] with
//! [`Choice`]s containing the full [`ChatMessage`](super::message::ChatMessage).
//! 3. **Streaming** — SSE chunks are fed into [`SseParser::feed`] which yields
//! [`StreamEvent`]s: token/text, reasoning, tool-call deltas, usage, done.
//!
//! # Components
//!
//! - `ChatRequest` / `StreamOptions` / `ToolDef` / `ToolFunctionDef` — outbound
//! - `ChatResponse` / `Choice` / `Delta` / `TokenUsage` — non-streaming inbound
//! - `StreamEvent` — one atomic streaming event (Token, Reasoning,
//! ToolCallDelta, Usage, Done, Error)
//! - `SseParser` — incremental SSE frame parser: `feed()` → `Vec<StreamEvent>`
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing;
// ---------------------------------------------------------------------------
// Chat request / response
@@ -11,23 +29,32 @@ use serde_json::Value;
/// provider.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatRequest {
/// Model identifier, e.g. `"anthropic/claude-opus-4-8"`.
pub model: String,
/// Full message history (system + user + assistant + tool turns).
pub messages: Vec<super::message::ChatMessage>,
/// Maximum number of output tokens.
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
/// Sampling temperature (0.0 2.0).
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
/// Tool definitions available to the model.
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<ToolDef>>,
/// Controls which (if any) function is called by the model.
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<Value>,
/// Whether to use SSE streaming (`true`) or a single response.
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
/// Nucleus sampling threshold.
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
/// Sequences where the model should stop generation.
#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Vec<String>>,
/// Additional streaming options (e.g. `include_usage`).
#[serde(skip_serializing_if = "Option::is_none")]
pub stream_options: Option<StreamOptions>,
}
@@ -42,38 +69,53 @@ pub struct StreamOptions {
/// Wire format for a single tool definition sent to the provider.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDef {
/// The tool type discriminator, e.g. `"function"`.
#[serde(rename = "type")]
pub type_: String,
/// The function definition (name, description, JSON schema).
pub function: ToolFunctionDef,
}
/// Name, description, and JSON schema parameters for a tool definition.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolFunctionDef {
/// The function name the model may invoke.
pub name: String,
/// Human-readable description of what the function does.
pub description: String,
/// JSON Schema object describing the expected arguments.
pub parameters: Value,
}
/// Non-streaming chat completion response returned by the provider.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatResponse {
/// Unique response identifier from the provider.
pub id: String,
/// Object type, e.g. `"chat.completion"`.
pub object: Option<String>,
/// Model identifier that produced this response.
pub model: String,
/// One or more completion candidates.
pub choices: Vec<Choice>,
/// Token usage statistics (prompt, completion, total).
pub usage: Option<TokenUsage>,
/// Unix-timestamp of response creation.
pub created: Option<i64>,
}
/// One completion candidate within a `ChatResponse.choices` list.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Choice {
/// Zero-based index of this choice in the candidate list.
pub index: u32,
/// Full message (non-streaming response).
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<super::message::ChatMessage>,
/// Incremental delta (streaming response).
#[serde(skip_serializing_if = "Option::is_none")]
pub delta: Option<Delta>,
/// Why the model stopped: `"stop"`, `"tool_calls"`, `"length"`, etc.
#[serde(skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<String>,
}
@@ -81,10 +123,13 @@ pub struct Choice {
/// Incremental delta emitted in a streaming SSE chunk.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Delta {
/// Role being set for the first streaming chunk.
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<super::message::Role>,
/// Incremental text content delta.
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
/// Incremental tool-call delta (partial name/arguments).
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<super::tool_call::ToolCall>>,
}
@@ -92,11 +137,16 @@ pub struct Delta {
/// Token counts and optional cost breakdown for a single completion request.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TokenUsage {
/// Tokens consumed by the prompt (input).
pub prompt_tokens: u32,
/// Tokens consumed by the completion (output).
pub completion_tokens: u32,
/// Sum of prompt + completion tokens.
pub total_tokens: u32,
/// Estimated cost for prompt tokens (provider-specific).
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_tokens_cost: Option<f64>,
/// Estimated cost for completion tokens (provider-specific).
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_tokens_cost: Option<f64>,
}
@@ -108,28 +158,41 @@ pub struct TokenUsage {
/// One atomic event extracted from an LLM streaming response stream.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StreamEvent {
/// An incremental text token.
Token(String),
/// An incremental reasoning token (Anthropic `reasoning_content`).
Reasoning(String),
/// An incremental tool-call delta (partial ID, name, or arguments).
ToolCallDelta {
/// Tool-call index (multiple calls in one response).
index: usize,
/// Optional tool-call ID (usually in the first delta for a call).
id: Option<String>,
/// Optional function name (usually in the first delta for a call).
name: Option<String>,
/// Partial JSON arguments delta for this tool call.
arguments_delta: String,
},
/// Final usage chunk with token counts.
Usage {
prompt_tokens: u64,
completion_tokens: u64,
total_tokens: u64,
},
/// Stream complete (all tokens have been delivered).
Done,
/// A stream-level error occurred.
Error(String),
}
/// Buffered SSE frame parser that accumulates raw `data:` lines and
/// flushes a `StreamEvent` on each blank-line boundary.
pub struct SseParser {
/// Leftover bytes from the last chunk that did not end with `\n`.
buffer: String,
/// The current `event:` type (set by `event:` lines, cleared on flush).
event_type: Option<String>,
/// Accumulated `data:` lines for the current event frame.
data_lines: Vec<String>,
}
@@ -1,6 +1,18 @@
//! Filesystem layout for zesdex's persistent and scratch data directories.
//!
//! # Flow
//!
//! [`Store::new`] resolves all paths from OS data dir / temp dir →
//! [`ensure_dirs`](Store::ensure_dirs) creates them on startup.
//!
//! # Components
//!
//! - `Store` — resolved path bundle (base, memory, scratch, images, downloads)
//! - `new` — path computation (no I/O)
//! - `ensure_dirs` — creates all directories if missing
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tracing;
/// Resolved paths for all data directories zesdex reads from and writes to.
///
@@ -8,10 +20,15 @@ use std::path::PathBuf;
/// where memory, scratch, session images, and downloads live.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Store {
/// Top-level data directory, e.g. `~/.local/share/zesdex`.
pub base_dir: PathBuf,
/// Temporary scratch root, usually under the OS temp dir.
pub scratch_root: PathBuf,
/// Directory for persistent memory files (`.md` summaries).
pub memory_dir: PathBuf,
/// Directory for per-session image snapshots.
pub session_images_dir: PathBuf,
/// Directory for downloaded files.
pub download_dir: PathBuf,
}
@@ -41,6 +58,7 @@ impl Store {
///
/// Return: `Err` on the first directory that fails to create.
pub fn ensure_dirs(&self) -> std::io::Result<()> {
tracing::debug!(base = %self.base_dir.display(), "ensuring store directories exist");
std::fs::create_dir_all(&self.base_dir)?;
std::fs::create_dir_all(&self.memory_dir)?;
std::fs::create_dir_all(&self.scratch_root)?;
@@ -1,25 +1,42 @@
//! Tool-call DTOs embedded in assistant chat messages.
//!
//! Flow: provider response/stream carries `tool_calls` on an assistant
//! message → deserialized into `ToolCall`/`ToolFunction` → harness resolves
//! `function.name` against `all_tools()` and runs it with
//! `sanitize_tool_arguments(function.arguments)`.
//! # Flow
//!
//! Provider response/stream carries `tool_calls` on an assistant message →
//! deserialized into [`ToolCall`]/[`ToolFunction`] → harness resolves the
//! function name against `all_tools()` and runs it after sanitizing arguments
//! via [`sanitize_tool_arguments`] (which handles string-encoded JSON,
//! control characters, and truncation).
//!
//! # Components
//!
//! - `ToolCall` — a single tool-invocation request (id + type + function)
//! - `ToolFunction` — function name + raw arguments Value
//! - `sanitize_tool_arguments` — normalizes argument shape, repairs truncation
//! - `repair_json` — closes unclosed strings/braces/brackets in truncated JSON
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing;
/// A single tool-call request emitted by the model in an assistant message.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
/// Unique identifier for this tool call (referenced by `ToolCallResult`).
pub id: String,
/// Discriminator, e.g. `"function"`.
#[serde(rename = "type")]
pub type_: String,
/// The function to invoke (name + arguments).
pub function: ToolFunction,
}
/// The function name and raw arguments payload for a `ToolCall`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolFunction {
/// The function/tool name to dispatch against.
pub name: String,
/// Arguments as a JSON Value (may be a string-encoded object before
/// `sanitize_tool_arguments` normalises it).
pub arguments: Value,
}
@@ -1,13 +1,29 @@
//! Record of one completed tool invocation, kept for transcript/history.
//!
//! # Flow
//!
//! Tool harness completes execution → creates [`ToolCallResult`] with output,
//! error flag, and wall-clock duration → appended to conversation history as
//! a `Tool`-role [`ChatMessage`](super::message::ChatMessage).
//!
//! # Components
//!
//! - `ToolCallResult` — tool name + output + error flag + duration
//! - `new` — convenience constructor
use serde::{Deserialize, Serialize};
/// Record of a completed tool invocation, kept for transcript/history.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallResult {
/// The `id` of the `ToolCall` this result responds to.
pub tool_call_id: String,
/// The name of the tool that was invoked.
pub tool_name: String,
/// The text output produced by the tool (or error message).
pub output: String,
/// Whether the tool exited with an error.
pub is_error: bool,
/// Wall-clock execution duration in milliseconds.
pub duration_ms: u64,
}
@@ -1,17 +1,34 @@
//! Token usage accounting shared by streaming and non-streaming responses.
//!
//! # Flow
//!
//! Accumulated across all LLM API calls in a session. Each response updates
//! the running totals; `last_*` fields capture the most recent call's values
//! for interpolation display. Persisted alongside other session metadata.
//!
//! # Components
//!
//! - `UsageStats` — cumulative token/latency counters
use serde::{Deserialize, Serialize};
/// Cumulative token/latency counters for a session, persisted alongside it.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
pub struct UsageStats {
/// Total tokens consumed as input (prompt).
pub tokens_in: u64,
/// Total tokens generated as output (completion).
pub tokens_out: u64,
/// Most recent call's input tokens (for live interpolation display).
#[serde(default)]
pub last_tokens_in: u64,
/// Most recent call's output tokens (for live interpolation display).
#[serde(default)]
pub last_tokens_out: u64,
/// Total number of LLM API calls made this session.
pub api_calls: u64,
/// Tokens consumed by auto-review subagent calls.
pub review_tokens: u64,
/// Total wall-clock time spent on LLM API calls (milliseconds).
pub total_ms: u64,
}
+9 -2
View File
@@ -1,5 +1,12 @@
//! Domain entity modules organised by concern — pure data structures with
//! serde serialisation and filesystem persistence (serde JSON + std::fs).
//! Domain entity modules organised by concern.
//!
//! All types here are pure data structures with serde serialization
//! and filesystem persistence (serde JSON + `std::fs`).
//!
//! # Sub-modules
//!
//! - [`auth`] — Session, SessionLock (authentication data)
//! - [`common`] — Conversation, Message, Provider, Store, ToolCall, ToolResult, Usage
pub mod auth;
pub mod common;
+12
View File
@@ -9,6 +9,18 @@
//!
//! This crate contains ALL domain entity types as pure data structures
//! with no business logic beyond constructor/accessor methods.
//!
//! # Modules
//!
//! - [`domain::auth`] — Authentication entities: `Session`, `SessionLock`
//! - [`domain::common`] — Shared domain entities: `Conversation`, `Message`,
//! `Provider`, `Store`, `ToolCall`, `ToolResult`, `Usage`
//!
//! # Flow
//!
//! External consumers (`zesdex-iam`, `zesdex-backend`) import re-exported
//! types via `use zesdex_entities::*`. No instantiation logic lives here —
//! only struct/enum definitions, their fields, and lightweight constructors.
pub mod domain;