1use crate::constants::reasoning;
4use crate::core::PromptCachingConfig;
5use serde::{Deserialize, Deserializer, Serialize};
6use serde_json::Value;
7use std::collections::{BTreeMap, HashMap};
8use std::fmt;
9use std::path::PathBuf;
10
11#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
17#[serde(rename_all = "lowercase")]
18#[derive(Default)]
19pub enum ReasoningEffortLevel {
20 None,
22 Minimal,
24 Low,
26 #[default]
28 Medium,
29 High,
31 XHigh,
33}
34
35impl ReasoningEffortLevel {
36 pub fn as_str(self) -> &'static str {
38 match self {
39 Self::None => "none",
40 Self::Minimal => "minimal",
41 Self::Low => reasoning::LOW,
42 Self::Medium => reasoning::MEDIUM,
43 Self::High => reasoning::HIGH,
44 Self::XHigh => "xhigh",
45 }
46 }
47
48 pub fn parse(value: &str) -> Option<Self> {
50 let normalized = value.trim();
51 if normalized.eq_ignore_ascii_case("none") {
52 Some(Self::None)
53 } else if normalized.eq_ignore_ascii_case("minimal") {
54 Some(Self::Minimal)
55 } else if normalized.eq_ignore_ascii_case(reasoning::LOW) {
56 Some(Self::Low)
57 } else if normalized.eq_ignore_ascii_case(reasoning::MEDIUM) {
58 Some(Self::Medium)
59 } else if normalized.eq_ignore_ascii_case(reasoning::HIGH) {
60 Some(Self::High)
61 } else if normalized.eq_ignore_ascii_case("xhigh") {
62 Some(Self::XHigh)
63 } else {
64 None
65 }
66 }
67
68 pub fn allowed_values() -> &'static [&'static str] {
70 reasoning::ALLOWED_LEVELS
71 }
72}
73
74#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
78#[serde(rename_all = "lowercase")]
79#[derive(Default)]
80pub enum SystemPromptMode {
81 Minimal,
84 Lightweight,
87 #[default]
90 Default,
91 Specialized,
94}
95
96impl SystemPromptMode {
97 pub fn as_str(self) -> &'static str {
99 match self {
100 Self::Minimal => "minimal",
101 Self::Lightweight => "lightweight",
102 Self::Default => "default",
103 Self::Specialized => "specialized",
104 }
105 }
106
107 pub fn parse(value: &str) -> Option<Self> {
109 let normalized = value.trim();
110 if normalized.eq_ignore_ascii_case("minimal") {
111 Some(Self::Minimal)
112 } else if normalized.eq_ignore_ascii_case("lightweight") {
113 Some(Self::Lightweight)
114 } else if normalized.eq_ignore_ascii_case("default") {
115 Some(Self::Default)
116 } else if normalized.eq_ignore_ascii_case("specialized") {
117 Some(Self::Specialized)
118 } else {
119 None
120 }
121 }
122
123 pub fn allowed_values() -> &'static [&'static str] {
125 &["minimal", "lightweight", "default", "specialized"]
126 }
127}
128
129impl fmt::Display for SystemPromptMode {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 f.write_str(self.as_str())
132 }
133}
134
135impl<'de> Deserialize<'de> for SystemPromptMode {
136 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
137 where
138 D: Deserializer<'de>,
139 {
140 let raw = String::deserialize(deserializer)?;
141 if let Some(parsed) = Self::parse(&raw) {
142 Ok(parsed)
143 } else {
144 Ok(Self::default())
145 }
146 }
147}
148
149#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
153#[serde(rename_all = "lowercase")]
154#[derive(Default)]
155pub enum ToolDocumentationMode {
156 Minimal,
159 Progressive,
162 #[default]
165 Full,
166}
167
168impl ToolDocumentationMode {
169 pub fn as_str(self) -> &'static str {
171 match self {
172 Self::Minimal => "minimal",
173 Self::Progressive => "progressive",
174 Self::Full => "full",
175 }
176 }
177
178 pub fn parse(value: &str) -> Option<Self> {
180 let normalized = value.trim();
181 if normalized.eq_ignore_ascii_case("minimal") {
182 Some(Self::Minimal)
183 } else if normalized.eq_ignore_ascii_case("progressive") {
184 Some(Self::Progressive)
185 } else if normalized.eq_ignore_ascii_case("full") {
186 Some(Self::Full)
187 } else {
188 None
189 }
190 }
191
192 pub fn allowed_values() -> &'static [&'static str] {
194 &["minimal", "progressive", "full"]
195 }
196}
197
198impl fmt::Display for ToolDocumentationMode {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 f.write_str(self.as_str())
201 }
202}
203
204impl<'de> Deserialize<'de> for ToolDocumentationMode {
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: Deserializer<'de>,
208 {
209 let raw = String::deserialize(deserializer)?;
210 if let Some(parsed) = Self::parse(&raw) {
211 Ok(parsed)
212 } else {
213 Ok(Self::default())
214 }
215 }
216}
217
218#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
221#[serde(rename_all = "lowercase")]
222#[derive(Default)]
223pub enum VerbosityLevel {
224 Low,
225 #[default]
226 Medium,
227 High,
228}
229
230impl VerbosityLevel {
231 pub fn as_str(self) -> &'static str {
233 match self {
234 Self::Low => "low",
235 Self::Medium => "medium",
236 Self::High => "high",
237 }
238 }
239
240 pub fn parse(value: &str) -> Option<Self> {
242 let normalized = value.trim();
243 if normalized.eq_ignore_ascii_case("low") {
244 Some(Self::Low)
245 } else if normalized.eq_ignore_ascii_case("medium") {
246 Some(Self::Medium)
247 } else if normalized.eq_ignore_ascii_case("high") {
248 Some(Self::High)
249 } else {
250 None
251 }
252 }
253
254 pub fn allowed_values() -> &'static [&'static str] {
256 &["low", "medium", "high"]
257 }
258}
259
260impl fmt::Display for VerbosityLevel {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 f.write_str(self.as_str())
263 }
264}
265
266impl<'de> Deserialize<'de> for VerbosityLevel {
267 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
268 where
269 D: Deserializer<'de>,
270 {
271 let raw = String::deserialize(deserializer)?;
272 if let Some(parsed) = Self::parse(&raw) {
273 Ok(parsed)
274 } else {
275 Ok(Self::default())
276 }
277 }
278}
279
280impl fmt::Display for ReasoningEffortLevel {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 f.write_str(self.as_str())
283 }
284}
285
286impl<'de> Deserialize<'de> for ReasoningEffortLevel {
287 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
288 where
289 D: Deserializer<'de>,
290 {
291 let raw = String::deserialize(deserializer)?;
292 if let Some(parsed) = Self::parse(&raw) {
293 Ok(parsed)
294 } else {
295 Ok(Self::default())
296 }
297 }
298}
299
300#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
303#[serde(rename_all = "lowercase")]
304#[derive(Default)]
305pub enum UiSurfacePreference {
306 #[default]
307 Auto,
308 Alternate,
309 Inline,
310}
311
312impl UiSurfacePreference {
313 pub fn as_str(self) -> &'static str {
315 match self {
316 Self::Auto => "auto",
317 Self::Alternate => "alternate",
318 Self::Inline => "inline",
319 }
320 }
321
322 pub fn parse(value: &str) -> Option<Self> {
324 let normalized = value.trim();
325 if normalized.eq_ignore_ascii_case("auto") {
326 Some(Self::Auto)
327 } else if normalized.eq_ignore_ascii_case("alternate")
328 || normalized.eq_ignore_ascii_case("alt")
329 {
330 Some(Self::Alternate)
331 } else if normalized.eq_ignore_ascii_case("inline") {
332 Some(Self::Inline)
333 } else {
334 None
335 }
336 }
337
338 pub fn allowed_values() -> &'static [&'static str] {
340 &["auto", "alternate", "inline"]
341 }
342}
343
344impl fmt::Display for UiSurfacePreference {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 f.write_str(self.as_str())
347 }
348}
349
350impl<'de> Deserialize<'de> for UiSurfacePreference {
351 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
352 where
353 D: Deserializer<'de>,
354 {
355 let raw = String::deserialize(deserializer)?;
356 if let Some(parsed) = Self::parse(&raw) {
357 Ok(parsed)
358 } else {
359 Ok(Self::default())
360 }
361 }
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
366pub enum ModelSelectionSource {
367 #[default]
369 WorkspaceConfig,
370 CliOverride,
372}
373
374#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
384#[serde(rename_all = "lowercase")]
385#[derive(Default)]
386pub enum EditingMode {
387 #[default]
390 Edit,
391 Plan,
395}
396
397impl EditingMode {
398 pub fn as_str(self) -> &'static str {
400 match self {
401 Self::Edit => "edit",
402 Self::Plan => "plan",
403 }
404 }
405
406 pub fn parse(value: &str) -> Option<Self> {
408 let normalized = value.trim();
409 if normalized.eq_ignore_ascii_case("edit") {
410 Some(Self::Edit)
411 } else if normalized.eq_ignore_ascii_case("plan") {
412 Some(Self::Plan)
413 } else {
414 None
415 }
416 }
417
418 pub fn allowed_values() -> &'static [&'static str] {
420 &["edit", "plan"]
421 }
422
423 pub fn can_modify_files(self) -> bool {
425 matches!(self, Self::Edit)
426 }
427
428 pub fn can_execute_commands(self) -> bool {
430 matches!(self, Self::Edit)
431 }
432
433 pub fn is_read_only(self) -> bool {
435 matches!(self, Self::Plan)
436 }
437}
438
439impl fmt::Display for EditingMode {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 f.write_str(self.as_str())
442 }
443}
444
445impl<'de> Deserialize<'de> for EditingMode {
446 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
447 where
448 D: Deserializer<'de>,
449 {
450 let raw = String::deserialize(deserializer)?;
451 if let Some(parsed) = Self::parse(&raw) {
452 Ok(parsed)
453 } else {
454 Ok(Self::default())
455 }
456 }
457}
458
459#[derive(Debug, Clone)]
461pub struct AgentConfig {
462 pub model: String,
463 pub api_key: String,
464 pub provider: String,
465 pub api_key_env: String,
466 pub workspace: std::path::PathBuf,
467 pub verbose: bool,
468 pub quiet: bool,
469 pub theme: String,
470 pub reasoning_effort: ReasoningEffortLevel,
471 pub ui_surface: UiSurfacePreference,
472 pub prompt_cache: PromptCachingConfig,
473 pub model_source: ModelSelectionSource,
474 pub custom_api_keys: BTreeMap<String, String>,
475 pub checkpointing_enabled: bool,
476 pub checkpointing_storage_dir: Option<PathBuf>,
477 pub checkpointing_max_snapshots: usize,
478 pub checkpointing_max_age_days: Option<u64>,
479 pub max_conversation_turns: usize,
480 pub model_behavior: Option<crate::core::ModelConfig>,
481}
482
483#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
485pub enum CapabilityLevel {
486 Basic,
488 FileReading,
490 FileListing,
492 Bash,
494 Editing,
496 CodeSearch,
498}
499
500#[derive(Debug, Clone, Serialize, Deserialize)]
502pub struct SessionInfo {
503 pub session_id: String,
504 pub start_time: u64,
505 pub total_turns: usize,
506 pub total_decisions: usize,
507 pub error_count: usize,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct ConversationTurn {
513 pub turn_number: usize,
514 pub timestamp: u64,
515 pub user_input: Option<String>,
516 pub agent_response: Option<String>,
517 pub tool_calls: Vec<ToolCallInfo>,
518 pub decision: Option<DecisionInfo>,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct ToolCallInfo {
524 pub name: String,
525 pub args: Value,
526 pub result: Option<Value>,
527 pub error: Option<String>,
528 pub execution_time_ms: Option<u64>,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct DecisionInfo {
534 pub turn_number: usize,
535 pub action_type: String,
536 pub description: String,
537 pub reasoning: String,
538 pub outcome: Option<String>,
539 pub confidence_score: Option<f64>,
540 pub timestamp: u64,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct ErrorInfo {
546 pub error_type: String,
547 pub message: String,
548 pub turn_number: usize,
549 pub recoverable: bool,
550 pub timestamp: u64,
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct TaskInfo {
556 pub task_type: String,
557 pub description: String,
558 pub completed: bool,
559 pub success: bool,
560 pub duration_seconds: Option<u64>,
561 pub tools_used: Vec<String>,
562 pub dependencies: Vec<String>,
563}
564
565#[derive(Debug, Clone, Serialize, Deserialize)]
567pub struct ProjectSpec {
568 pub name: String,
569 pub features: Vec<String>,
570 pub template: Option<String>,
571 pub dependencies: HashMap<String, String>,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
576pub struct WorkspaceAnalysis {
577 pub root_path: String,
578 pub project_type: Option<String>,
579 pub languages: Vec<String>,
580 pub frameworks: Vec<String>,
581 pub config_files: Vec<String>,
582 pub source_files: Vec<String>,
583 pub test_files: Vec<String>,
584 pub documentation_files: Vec<String>,
585 pub total_files: usize,
586 pub total_size_bytes: u64,
587}
588
589#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct CommandResult {
592 pub command: String,
593 pub success: bool,
594 pub stdout: String,
595 pub stderr: String,
596 pub exit_code: Option<i32>,
597 pub execution_time_ms: u64,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
602pub struct FileOperationResult {
603 pub operation: String,
604 pub path: String,
605 pub success: bool,
606 pub details: HashMap<String, Value>,
607 pub error: Option<String>,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct PerformanceMetrics {
613 pub session_duration_seconds: u64,
614 pub total_api_calls: usize,
615 pub total_tokens_used: Option<usize>,
616 pub average_response_time_ms: f64,
617 pub tool_execution_count: usize,
618 pub error_count: usize,
619 pub recovery_success_rate: f64,
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct QualityMetrics {
625 pub decision_confidence_avg: f64,
626 pub tool_success_rate: f64,
627 pub error_recovery_rate: f64,
628 pub context_preservation_rate: f64,
629 pub user_satisfaction_score: Option<f64>,
630}
631
632#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct ToolConfig {
635 pub enable_validation: bool,
636 pub max_execution_time_seconds: u64,
637 pub allow_file_creation: bool,
638 pub allow_file_deletion: bool,
639 pub working_directory: Option<String>,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct ContextConfig {
645 pub max_context_length: usize,
646 pub compression_threshold: usize,
647 pub summarization_interval: usize,
648 pub preservation_priority: Vec<String>,
649}
650
651#[derive(Debug, Clone, Serialize, Deserialize)]
653pub struct LoggingConfig {
654 pub level: String,
655 pub file_logging: bool,
656 pub log_directory: Option<String>,
657 pub max_log_files: usize,
658 pub max_log_size_mb: usize,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
663pub enum AnalysisDepth {
664 Basic,
665 Standard,
666 Deep,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
671pub enum OutputFormat {
672 Text,
673 Json,
674 Html,
675}
676
677#[derive(Debug, Clone, Serialize, Deserialize)]
679pub enum CompressionLevel {
680 Light,
681 Medium,
682 Aggressive,
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688
689 #[test]
690 fn test_editing_mode_parse() {
691 assert_eq!(EditingMode::parse("edit"), Some(EditingMode::Edit));
692 assert_eq!(EditingMode::parse("EDIT"), Some(EditingMode::Edit));
693 assert_eq!(EditingMode::parse("Edit"), Some(EditingMode::Edit));
694 assert_eq!(EditingMode::parse("plan"), Some(EditingMode::Plan));
695 assert_eq!(EditingMode::parse("PLAN"), Some(EditingMode::Plan));
696 assert_eq!(EditingMode::parse("Plan"), Some(EditingMode::Plan));
697 assert_eq!(EditingMode::parse("agent"), None);
698 assert_eq!(EditingMode::parse("invalid"), None);
699 assert_eq!(EditingMode::parse(""), None);
700 }
701
702 #[test]
703 fn test_editing_mode_as_str() {
704 assert_eq!(EditingMode::Edit.as_str(), "edit");
705 assert_eq!(EditingMode::Plan.as_str(), "plan");
706 }
707
708 #[test]
709 fn test_editing_mode_capabilities() {
710 assert!(EditingMode::Edit.can_modify_files());
712 assert!(EditingMode::Edit.can_execute_commands());
713 assert!(!EditingMode::Edit.is_read_only());
714
715 assert!(!EditingMode::Plan.can_modify_files());
717 assert!(!EditingMode::Plan.can_execute_commands());
718 assert!(EditingMode::Plan.is_read_only());
719 }
720
721 #[test]
722 fn test_editing_mode_default() {
723 assert_eq!(EditingMode::default(), EditingMode::Edit);
724 }
725
726 #[test]
727 fn test_editing_mode_display() {
728 assert_eq!(format!("{}", EditingMode::Edit), "edit");
729 assert_eq!(format!("{}", EditingMode::Plan), "plan");
730 }
731
732 #[test]
733 fn test_editing_mode_allowed_values() {
734 let values = EditingMode::allowed_values();
735 assert_eq!(values, &["edit", "plan"]);
736 }
737}