//! Per-session runtime state: message history, pending tool queue, //! background bash jobs, lesson/review counters, and the `TurnEvent` //! stream emitted while an agent turn is in flight. use serde::{Deserialize, Serialize}; use std::path::PathBuf; pub use zesdex_entities::seaorm::common::usage::UsageStats; /// Mutable, serializable state for one session: chat history, tool /// results, pending tools, background jobs, and lesson/review counters /// shown in the TUI status bar. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SessionRuntime { pub messages: Vec, pub tool_call_results: Vec, pub pending_tool_queue: Vec, pub bash_jobs: Vec, pub subagent_queue: usize, pub edit_count: u32, pub consecutive_empty_reviews: u32, pub session_start: i64, pub lesson_count: u32, pub lessons_user: u32, pub lessons_feedback: u32, pub lessons_project: u32, pub lessons_reference: u32, pub lessons_active: u32, pub lessons_stale: u32, pub lessons_contradicted: u32, pub lessons_human: u32, pub lessons_verified: u32, pub lessons_unverified: u32, pub review_count: u32, pub session_dir: PathBuf, pub usage: UsageStats, /// Whether a hive-mind convergence has completed at least once in this /// session. Set by the main-thread event loop when it receives a /// `TurnEvent::SystemNote { kind: "hive_mind_converged", .. }` — the /// only reliable way to detect this across turns, since system messages /// pushed mid-turn inside `run_agent_turn` are NOT persisted into /// `rt.messages` (they stay local to that turn's background thread and /// are only archived to `SQLite`). pub hive_mind_converged: bool, } pub use zesdex_entities::seaorm::common::tool_result::ToolCallResult; /// A tool call awaiting execution, along with which execution model /// (inline, deferred, async) it should run under. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PendingTool { pub tool_name: String, pub args: serde_json::Value, pub execution_model: crate::app::state::types::ExecutionModel, } /// Reference to a background bash job tracked in session state (the actual /// process handle lives elsewhere; this is just the display/status record). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BashJobRef { pub id: String, pub command: String, pub started_at: i64, pub running: bool, } /// Events emitted onto the turn-event queue while an agent turn runs, /// consumed by the event loop to update state and drive re-renders. #[derive(Debug, Clone)] pub enum TurnEvent { AssistantMessage(crate::dto::chat::message::ChatMessage), ToolResult { tool_call_id: String, tool_name: String, output: String, is_error: bool, path: Option, }, SystemNote { kind: String, message: String, }, StreamStart, StreamToken(String), StreamDone(crate::dto::chat::message::ChatMessage), Usage { tokens_in: u64, tokens_out: u64, }, /// Token usage from a subagent (review, test-gen, arch-review, etc.) /// routed to `UsageStats::review_tokens` so the Usage panel can split /// "main" tokens from "self-learning" tokens. Same shape as `Usage` but /// kept as a distinct variant so future subagent-specific metadata /// (origin tag, subagent name) can be attached without breaking the /// main-agent path. ReviewUsage { tokens_in: u64, tokens_out: u64, }, Compacted(Vec), Error(String), Done, /// Real-time update from a workflow subagent: push the new status /// into `AppStateRest::workflow_engine.agents`. WorkflowAgentUpdate { agent_id: String, agent_name: String, status: crate::app::workflow::engine::AgentStatus, }, } impl SessionRuntime { /// Create fresh runtime state for a session rooted at `session_dir`, /// with all counters zeroed and `session_start` set to now. pub fn new(session_dir: PathBuf) -> Self { SessionRuntime { messages: Vec::new(), tool_call_results: Vec::new(), pending_tool_queue: Vec::new(), bash_jobs: Vec::new(), subagent_queue: 0, edit_count: 0, consecutive_empty_reviews: 0, session_start: chrono::Utc::now().timestamp_millis(), lesson_count: 0, lessons_user: 0, lessons_feedback: 0, lessons_project: 0, lessons_reference: 0, lessons_active: 0, lessons_stale: 0, lessons_contradicted: 0, lessons_human: 0, lessons_verified: 0, lessons_unverified: 0, review_count: 0, session_dir, usage: UsageStats::default(), hive_mind_converged: false, } } /// Append a message to the session's conversation history. pub fn push_message(&mut self, msg: crate::dto::chat::message::ChatMessage) { self.messages.push(msg); } }