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}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
484pub enum CapabilityLevel {
485 Basic,
487 FileReading,
489 FileListing,
491 Bash,
493 Editing,
495 CodeSearch,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
501pub struct SessionInfo {
502 pub session_id: String,
503 pub start_time: u64,
504 pub total_turns: usize,
505 pub total_decisions: usize,
506 pub error_count: usize,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct ConversationTurn {
512 pub turn_number: usize,
513 pub timestamp: u64,
514 pub user_input: Option<String>,
515 pub agent_response: Option<String>,
516 pub tool_calls: Vec<ToolCallInfo>,
517 pub decision: Option<DecisionInfo>,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct ToolCallInfo {
523 pub name: String,
524 pub args: Value,
525 pub result: Option<Value>,
526 pub error: Option<String>,
527 pub execution_time_ms: Option<u64>,
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize)]
532pub struct DecisionInfo {
533 pub turn_number: usize,
534 pub action_type: String,
535 pub description: String,
536 pub reasoning: String,
537 pub outcome: Option<String>,
538 pub confidence_score: Option<f64>,
539 pub timestamp: u64,
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize)]
544pub struct ErrorInfo {
545 pub error_type: String,
546 pub message: String,
547 pub turn_number: usize,
548 pub recoverable: bool,
549 pub timestamp: u64,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct TaskInfo {
555 pub task_type: String,
556 pub description: String,
557 pub completed: bool,
558 pub success: bool,
559 pub duration_seconds: Option<u64>,
560 pub tools_used: Vec<String>,
561 pub dependencies: Vec<String>,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct ProjectSpec {
567 pub name: String,
568 pub features: Vec<String>,
569 pub template: Option<String>,
570 pub dependencies: HashMap<String, String>,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
575pub struct WorkspaceAnalysis {
576 pub root_path: String,
577 pub project_type: Option<String>,
578 pub languages: Vec<String>,
579 pub frameworks: Vec<String>,
580 pub config_files: Vec<String>,
581 pub source_files: Vec<String>,
582 pub test_files: Vec<String>,
583 pub documentation_files: Vec<String>,
584 pub total_files: usize,
585 pub total_size_bytes: u64,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct CommandResult {
591 pub command: String,
592 pub success: bool,
593 pub stdout: String,
594 pub stderr: String,
595 pub exit_code: Option<i32>,
596 pub execution_time_ms: u64,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct FileOperationResult {
602 pub operation: String,
603 pub path: String,
604 pub success: bool,
605 pub details: HashMap<String, Value>,
606 pub error: Option<String>,
607}
608
609#[derive(Debug, Clone, Serialize, Deserialize)]
611pub struct PerformanceMetrics {
612 pub session_duration_seconds: u64,
613 pub total_api_calls: usize,
614 pub total_tokens_used: Option<usize>,
615 pub average_response_time_ms: f64,
616 pub tool_execution_count: usize,
617 pub error_count: usize,
618 pub recovery_success_rate: f64,
619}
620
621#[derive(Debug, Clone, Serialize, Deserialize)]
623pub struct QualityMetrics {
624 pub decision_confidence_avg: f64,
625 pub tool_success_rate: f64,
626 pub error_recovery_rate: f64,
627 pub context_preservation_rate: f64,
628 pub user_satisfaction_score: Option<f64>,
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize)]
633pub struct ToolConfig {
634 pub enable_validation: bool,
635 pub max_execution_time_seconds: u64,
636 pub allow_file_creation: bool,
637 pub allow_file_deletion: bool,
638 pub working_directory: Option<String>,
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize)]
643pub struct ContextConfig {
644 pub max_context_length: usize,
645 pub compression_threshold: usize,
646 pub summarization_interval: usize,
647 pub preservation_priority: Vec<String>,
648}
649
650#[derive(Debug, Clone, Serialize, Deserialize)]
652pub struct LoggingConfig {
653 pub level: String,
654 pub file_logging: bool,
655 pub log_directory: Option<String>,
656 pub max_log_files: usize,
657 pub max_log_size_mb: usize,
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize)]
662pub enum AnalysisDepth {
663 Basic,
664 Standard,
665 Deep,
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize)]
670pub enum OutputFormat {
671 Text,
672 Json,
673 Html,
674}
675
676#[derive(Debug, Clone, Serialize, Deserialize)]
678pub enum CompressionLevel {
679 Light,
680 Medium,
681 Aggressive,
682}
683
684#[cfg(test)]
685mod tests {
686 use super::*;
687
688 #[test]
689 fn test_editing_mode_parse() {
690 assert_eq!(EditingMode::parse("edit"), Some(EditingMode::Edit));
691 assert_eq!(EditingMode::parse("EDIT"), Some(EditingMode::Edit));
692 assert_eq!(EditingMode::parse("Edit"), Some(EditingMode::Edit));
693 assert_eq!(EditingMode::parse("plan"), Some(EditingMode::Plan));
694 assert_eq!(EditingMode::parse("PLAN"), Some(EditingMode::Plan));
695 assert_eq!(EditingMode::parse("Plan"), Some(EditingMode::Plan));
696 assert_eq!(EditingMode::parse("agent"), None);
697 assert_eq!(EditingMode::parse("invalid"), None);
698 assert_eq!(EditingMode::parse(""), None);
699 }
700
701 #[test]
702 fn test_editing_mode_as_str() {
703 assert_eq!(EditingMode::Edit.as_str(), "edit");
704 assert_eq!(EditingMode::Plan.as_str(), "plan");
705 }
706
707 #[test]
708 fn test_editing_mode_capabilities() {
709 assert!(EditingMode::Edit.can_modify_files());
711 assert!(EditingMode::Edit.can_execute_commands());
712 assert!(!EditingMode::Edit.is_read_only());
713
714 assert!(!EditingMode::Plan.can_modify_files());
716 assert!(!EditingMode::Plan.can_execute_commands());
717 assert!(EditingMode::Plan.is_read_only());
718 }
719
720 #[test]
721 fn test_editing_mode_default() {
722 assert_eq!(EditingMode::default(), EditingMode::Edit);
723 }
724
725 #[test]
726 fn test_editing_mode_display() {
727 assert_eq!(format!("{}", EditingMode::Edit), "edit");
728 assert_eq!(format!("{}", EditingMode::Plan), "plan");
729 }
730
731 #[test]
732 fn test_editing_mode_allowed_values() {
733 let values = EditingMode::allowed_values();
734 assert_eq!(values, &["edit", "plan"]);
735 }
736}