ares/db/
agent_versions.rs1use anyhow::Result;
10use sqlx::PgPool;
11use tracing::{info, instrument, warn};
12
13use crate::utils::toon_config::ToonAgentConfig;
14
15#[instrument(skip(pool, agents), fields(count = agents.len()))]
21pub async fn record_agent_versions(
22 pool: &PgPool,
23 agents: &[ToonAgentConfig],
24 change_source: &str,
25) -> Result<()> {
26 let mut recorded = 0usize;
27
28 for agent in agents {
29 let config_json = serde_json::to_value(agent)
30 .unwrap_or_else(|_| serde_json::json!({"name": agent.name}));
31
32 let sql = if change_source == "rollback" {
36 "INSERT INTO agent_config_versions \
37 (agent_id, version, config_json, is_active, change_source) \
38 VALUES ($1, $2, $3, true, $4) \
39 ON CONFLICT (agent_id, version) DO UPDATE \
40 SET change_source = EXCLUDED.change_source, is_active = true"
41 } else {
42 "INSERT INTO agent_config_versions \
43 (agent_id, version, config_json, is_active, change_source) \
44 VALUES ($1, $2, $3, true, $4) \
45 ON CONFLICT (agent_id, version) DO NOTHING"
46 };
47
48 match sqlx::query(sql)
49 .bind(&agent.name)
50 .bind(&agent.version)
51 .bind(&config_json)
52 .bind(change_source)
53 .execute(pool)
54 .await
55 {
56 Ok(r) if r.rows_affected() > 0 => {
57 recorded += 1;
58 }
59 Ok(_) => {
60 }
62 Err(e) => {
63 warn!(
64 agent = %agent.name,
65 version = %agent.version,
66 error = %e,
67 "Failed to record agent version"
68 );
69 }
70 }
71 }
72
73 if recorded > 0 {
74 info!(
75 recorded,
76 source = change_source,
77 "Agent config versions recorded"
78 );
79 }
80
81 Ok(())
82}
83
84pub async fn get_agent_version_history(
86 pool: &PgPool,
87 agent_id: &str,
88 limit: i64,
89) -> Result<Vec<AgentVersionRecord>> {
90 let rows = sqlx::query_as::<_, AgentVersionRecord>(
94 r#"SELECT id, agent_id, version, config_json, is_active, change_source, created_at
95 FROM agent_config_versions
96 WHERE agent_id = $1
97 ORDER BY created_at DESC
98 LIMIT $2"#,
99 )
100 .bind(agent_id)
101 .bind(limit)
102 .fetch_all(pool)
103 .await?;
104
105 Ok(rows)
106}
107
108#[derive(Debug, Clone, serde::Serialize, sqlx::FromRow)]
110pub struct AgentVersionRecord {
111 pub id: String,
112 pub agent_id: String,
113 pub version: String,
114 pub config_json: serde_json::Value,
115 pub is_active: bool,
116 pub change_source: String,
117 pub created_at: chrono::DateTime<chrono::Utc>,
118}