coro_core/agent/
state.rs

1//! Persisted agent context snapshot for export/import
2//!
3//! This module defines a lightweight snapshot structure for exporting the
4//! essential conversation state and execution context of an agent, and helpers
5//! to serialize/deserialize it to/from JSON or files. It is intentionally
6//! independent of any live resources (LLM client, tool registry, etc.).
7
8use crate::agent::config::AgentConfig;
9use crate::llm::LlmMessage;
10use crate::output::AgentExecutionContext;
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use std::path::Path;
14
15/// A versioned, serializable snapshot of an agent's context
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct PersistedAgentContext {
18    /// Snapshot version for forward compatibility
19    pub version: u32,
20    /// Implementation identifier (e.g., "coro_agent")
21    pub agent_type: String,
22    /// When this snapshot was saved
23    pub saved_at: DateTime<Utc>,
24    /// Optional agent configuration captured at the time of saving
25    pub config: Option<AgentConfig>,
26    /// Full conversation message history (including system/user/assistant/tool)
27    pub conversation_history: Vec<LlmMessage>,
28    /// Execution context: goal, current task, token usage, etc.
29    pub execution_context: Option<AgentExecutionContext>,
30}
31
32impl PersistedAgentContext {
33    /// Create a new snapshot
34    pub fn new(
35        agent_type: String,
36        config: Option<AgentConfig>,
37        conversation_history: Vec<LlmMessage>,
38        execution_context: Option<AgentExecutionContext>,
39    ) -> Self {
40        Self {
41            version: 1,
42            agent_type,
43            saved_at: Utc::now(),
44            config,
45            conversation_history,
46            execution_context,
47        }
48    }
49
50    /// Serialize the snapshot to a JSON string
51    pub fn to_json(&self) -> crate::error::Result<String> {
52        Ok(serde_json::to_string_pretty(self)?)
53    }
54
55    /// Deserialize a snapshot from a JSON string
56    pub fn from_json(s: &str) -> crate::error::Result<Self> {
57        Ok(serde_json::from_str::<Self>(s)?)
58    }
59
60    /// Save the snapshot to a file (creates parent directories if needed)
61    pub fn to_file(&self, path: &Path) -> crate::error::Result<()> {
62        if let Some(parent) = path.parent() {
63            if !parent.as_os_str().is_empty() {
64                std::fs::create_dir_all(parent)?;
65            }
66        }
67        let json = self.to_json()?;
68        std::fs::write(path, json)?;
69        Ok(())
70    }
71
72    /// Load a snapshot from a file
73    pub fn from_file(path: &Path) -> crate::error::Result<Self> {
74        let data = std::fs::read_to_string(path)?;
75        Self::from_json(&data)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn round_trip_json() {
85        let snapshot = PersistedAgentContext::new(
86            "coro_agent".to_string(),
87            Some(AgentConfig::default()),
88            vec![LlmMessage::system("hello"), LlmMessage::assistant("world")],
89            Some(AgentExecutionContext {
90                agent_id: "coro_agent".to_string(),
91                original_goal: "OG".to_string(),
92                current_task: "CT".to_string(),
93                project_path: ".".to_string(),
94                max_steps: 5,
95                current_step: 1,
96                execution_time: std::time::Duration::from_secs(0),
97                token_usage: Default::default(),
98            }),
99        );
100
101        let json = snapshot.to_json().expect("serialize");
102        let restored = PersistedAgentContext::from_json(&json).expect("deserialize");
103
104        assert_eq!(restored.version, 1);
105        assert_eq!(restored.agent_type, "coro_agent");
106        assert_eq!(restored.conversation_history.len(), 2);
107        assert!(restored.execution_context.is_some());
108        assert!(restored.config.is_some());
109    }
110}