agent_teams/models/
session.rs1use std::collections::HashMap;
4use std::path::PathBuf;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14pub struct SessionState {
15 pub name: String,
17 pub backend_type: String,
19 pub prompt: String,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub model: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub cwd: Option<PathBuf>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub max_turns: Option<i32>,
30 #[serde(default, skip_serializing_if = "Vec::is_empty")]
32 pub allowed_tools: Vec<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub permission_mode: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub reasoning_effort: Option<String>,
39 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
41 pub env: HashMap<String, String>,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub memory_config: Option<crate::memory::MemoryConfig>,
45 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
47 pub metadata: HashMap<String, String>,
48 pub created_at: chrono::DateTime<chrono::Utc>,
50}
51
52impl SessionState {
53 pub fn from_config(
55 config: &crate::backend::SpawnConfig,
56 backend_type: &crate::backend::BackendType,
57 ) -> Self {
58 Self {
59 name: config.name.clone(),
60 backend_type: backend_type.to_string(),
61 prompt: config.prompt.clone(),
62 model: config.model.clone(),
63 cwd: config.cwd.clone(),
64 max_turns: config.max_turns,
65 allowed_tools: config.allowed_tools.clone(),
66 permission_mode: config.permission_mode.clone(),
67 reasoning_effort: config.reasoning_effort.clone(),
68 env: config.env.clone(),
69 memory_config: config.memory_config.clone(),
70 metadata: HashMap::new(),
73 created_at: chrono::Utc::now(),
74 }
75 }
76
77 pub fn to_spawn_config(&self) -> crate::backend::SpawnConfig {
79 crate::backend::SpawnConfig {
80 name: self.name.clone(),
81 prompt: self.prompt.clone(),
82 model: self.model.clone(),
83 cwd: self.cwd.clone(),
84 max_turns: self.max_turns,
85 allowed_tools: self.allowed_tools.clone(),
86 permission_mode: self.permission_mode.clone(),
87 reasoning_effort: self.reasoning_effort.clone(),
88 env: self.env.clone(),
89 memory_config: self.memory_config.clone(),
90 delegations: Vec::new(),
93 }
94 }
95
96 pub fn parse_backend_type(&self) -> Option<crate::backend::BackendType> {
100 self.backend_type.parse().ok()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn session_state_round_trip() {
110 let state = SessionState {
111 name: "test-agent".into(),
112 backend_type: "codex".into(),
113 prompt: "You are a test assistant.".into(),
114 model: Some("gpt-4.1".into()),
115 cwd: Some(PathBuf::from("/tmp")),
116 max_turns: None,
117 allowed_tools: vec![],
118 permission_mode: None,
119 reasoning_effort: Some("medium".into()),
120 env: HashMap::new(),
121 memory_config: None,
122 metadata: {
123 let mut m = HashMap::new();
124 m.insert("thread_id".into(), "abc-123".into());
125 m
126 },
127 created_at: chrono::Utc::now(),
128 };
129
130 let json = serde_json::to_string_pretty(&state).unwrap();
131 let parsed: SessionState = serde_json::from_str(&json).unwrap();
132
133 assert_eq!(parsed.name, "test-agent");
134 assert_eq!(parsed.backend_type, "codex");
135 assert_eq!(parsed.model.as_deref(), Some("gpt-4.1"));
136 assert_eq!(parsed.metadata.get("thread_id").unwrap(), "abc-123");
137 }
138
139 #[test]
140 fn from_config_and_back() {
141 let config = crate::backend::SpawnConfig::new("reviewer", "You review code.");
142 let state = SessionState::from_config(&config, &crate::backend::BackendType::GeminiCli);
143
144 assert_eq!(state.name, "reviewer");
145 assert_eq!(state.backend_type, "gemini-cli");
146 assert_eq!(state.parse_backend_type(), Some(crate::backend::BackendType::GeminiCli));
147
148 let restored = state.to_spawn_config();
149 assert_eq!(restored.name, "reviewer");
150 assert_eq!(restored.prompt, "You review code.");
151 }
152}