1use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use time::OffsetDateTime;
16use uuid::Uuid;
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub struct ThreadId(pub String);
21
22impl ThreadId {
23 #[must_use]
24 pub fn new() -> Self {
25 Self(Uuid::new_v4().to_string())
26 }
27
28 #[must_use]
29 pub fn from_string(s: impl Into<String>) -> Self {
30 Self(s.into())
31 }
32}
33
34impl Default for ThreadId {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl std::fmt::Display for ThreadId {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "{}", self.0)
43 }
44}
45
46#[derive(Clone, Debug)]
48pub struct AgentConfig {
49 pub max_turns: usize,
51 pub max_tokens: u32,
53 pub system_prompt: String,
55 pub model: String,
57 pub retry: RetryConfig,
59}
60
61impl Default for AgentConfig {
62 fn default() -> Self {
63 Self {
64 max_turns: 10,
65 max_tokens: 4096,
66 system_prompt: String::new(),
67 model: String::from("claude-sonnet-4-20250514"),
68 retry: RetryConfig::default(),
69 }
70 }
71}
72
73#[derive(Clone, Debug)]
75pub struct RetryConfig {
76 pub max_retries: u32,
78 pub base_delay_ms: u64,
80 pub max_delay_ms: u64,
82}
83
84impl Default for RetryConfig {
85 fn default() -> Self {
86 Self {
87 max_retries: 5,
88 base_delay_ms: 1000,
89 max_delay_ms: 120_000,
90 }
91 }
92}
93
94impl RetryConfig {
95 #[must_use]
97 pub const fn no_retry() -> Self {
98 Self {
99 max_retries: 0,
100 base_delay_ms: 0,
101 max_delay_ms: 0,
102 }
103 }
104
105 #[must_use]
107 pub const fn fast() -> Self {
108 Self {
109 max_retries: 5,
110 base_delay_ms: 10,
111 max_delay_ms: 100,
112 }
113 }
114}
115
116#[derive(Clone, Debug, Default, Serialize, Deserialize)]
118pub struct TokenUsage {
119 pub input_tokens: u32,
120 pub output_tokens: u32,
121}
122
123impl TokenUsage {
124 pub const fn add(&mut self, other: &Self) {
125 self.input_tokens += other.input_tokens;
126 self.output_tokens += other.output_tokens;
127 }
128}
129
130#[derive(Clone, Debug, Serialize, Deserialize)]
132pub struct ToolResult {
133 pub success: bool,
135 pub output: String,
137 pub data: Option<serde_json::Value>,
139 pub duration_ms: Option<u64>,
141}
142
143impl ToolResult {
144 #[must_use]
145 pub fn success(output: impl Into<String>) -> Self {
146 Self {
147 success: true,
148 output: output.into(),
149 data: None,
150 duration_ms: None,
151 }
152 }
153
154 #[must_use]
155 pub fn success_with_data(output: impl Into<String>, data: serde_json::Value) -> Self {
156 Self {
157 success: true,
158 output: output.into(),
159 data: Some(data),
160 duration_ms: None,
161 }
162 }
163
164 #[must_use]
165 pub fn error(message: impl Into<String>) -> Self {
166 Self {
167 success: false,
168 output: message.into(),
169 data: None,
170 duration_ms: None,
171 }
172 }
173
174 #[must_use]
175 pub const fn with_duration(mut self, duration_ms: u64) -> Self {
176 self.duration_ms = Some(duration_ms);
177 self
178 }
179}
180
181#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
183pub enum ToolTier {
184 Observe,
186 Confirm,
188 RequiresPin,
190}
191
192#[derive(Clone, Debug, Serialize, Deserialize)]
194pub struct PendingAction {
195 pub id: String,
196 pub tool_name: String,
197 pub tool_input: serde_json::Value,
198 pub tier: ToolTier,
199 #[serde(with = "time::serde::rfc3339")]
200 pub created_at: OffsetDateTime,
201 #[serde(with = "time::serde::rfc3339")]
202 pub expires_at: OffsetDateTime,
203}
204
205#[derive(Clone, Debug, Serialize, Deserialize)]
207pub struct AgentState {
208 pub thread_id: ThreadId,
209 pub turn_count: usize,
210 pub total_usage: TokenUsage,
211 pub metadata: HashMap<String, serde_json::Value>,
212 #[serde(with = "time::serde::rfc3339")]
213 pub created_at: OffsetDateTime,
214}
215
216impl AgentState {
217 #[must_use]
218 pub fn new(thread_id: ThreadId) -> Self {
219 Self {
220 thread_id,
221 turn_count: 0,
222 total_usage: TokenUsage::default(),
223 metadata: HashMap::new(),
224 created_at: OffsetDateTime::now_utc(),
225 }
226 }
227}