1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct PersistedAgentContext {
18 pub version: u32,
20 pub agent_type: String,
22 pub saved_at: DateTime<Utc>,
24 pub config: Option<AgentConfig>,
26 pub conversation_history: Vec<LlmMessage>,
28 pub execution_context: Option<AgentExecutionContext>,
30}
31
32impl PersistedAgentContext {
33 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 pub fn to_json(&self) -> crate::error::Result<String> {
52 Ok(serde_json::to_string_pretty(self)?)
53 }
54
55 pub fn from_json(s: &str) -> crate::error::Result<Self> {
57 Ok(serde_json::from_str::<Self>(s)?)
58 }
59
60 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 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}