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 Agent,
398}
399
400impl EditingMode {
401 pub fn as_str(self) -> &'static str {
403 match self {
404 Self::Edit => "edit",
405 Self::Plan => "plan",
406 Self::Agent => "agent",
407 }
408 }
409
410 pub fn parse(value: &str) -> Option<Self> {
412 let normalized = value.trim();
413 if normalized.eq_ignore_ascii_case("edit") {
414 Some(Self::Edit)
415 } else if normalized.eq_ignore_ascii_case("plan") {
416 Some(Self::Plan)
417 } else if normalized.eq_ignore_ascii_case("agent") {
418 Some(Self::Agent)
419 } else {
420 None
421 }
422 }
423
424 pub fn allowed_values() -> &'static [&'static str] {
426 &["edit", "plan", "agent"]
427 }
428
429 pub fn can_modify_files(self) -> bool {
431 matches!(self, Self::Edit | Self::Agent)
432 }
433
434 pub fn can_execute_commands(self) -> bool {
436 matches!(self, Self::Edit | Self::Agent)
437 }
438
439 pub fn is_read_only(self) -> bool {
441 matches!(self, Self::Plan)
442 }
443
444 pub fn is_autonomous(self) -> bool {
446 matches!(self, Self::Agent)
447 }
448}
449
450impl fmt::Display for EditingMode {
451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452 f.write_str(self.as_str())
453 }
454}
455
456impl<'de> Deserialize<'de> for EditingMode {
457 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
458 where
459 D: Deserializer<'de>,
460 {
461 let raw = String::deserialize(deserializer)?;
462 if let Some(parsed) = Self::parse(&raw) {
463 Ok(parsed)
464 } else {
465 Ok(Self::default())
466 }
467 }
468}
469
470#[derive(Debug, Clone)]
472pub struct AgentConfig {
473 pub model: String,
474 pub api_key: String,
475 pub provider: String,
476 pub api_key_env: String,
477 pub workspace: std::path::PathBuf,
478 pub verbose: bool,
479 pub quiet: bool,
480 pub theme: String,
481 pub reasoning_effort: ReasoningEffortLevel,
482 pub ui_surface: UiSurfacePreference,
483 pub prompt_cache: PromptCachingConfig,
484 pub model_source: ModelSelectionSource,
485 pub custom_api_keys: BTreeMap<String, String>,
486 pub checkpointing_enabled: bool,
487 pub checkpointing_storage_dir: Option<PathBuf>,
488 pub checkpointing_max_snapshots: usize,
489 pub checkpointing_max_age_days: Option<u64>,
490 pub max_conversation_turns: usize,
491}
492
493#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
495pub enum CapabilityLevel {
496 Basic,
498 FileReading,
500 FileListing,
502 Bash,
504 Editing,
506 CodeSearch,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct SessionInfo {
513 pub session_id: String,
514 pub start_time: u64,
515 pub total_turns: usize,
516 pub total_decisions: usize,
517 pub error_count: usize,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct ConversationTurn {
523 pub turn_number: usize,
524 pub timestamp: u64,
525 pub user_input: Option<String>,
526 pub agent_response: Option<String>,
527 pub tool_calls: Vec<ToolCallInfo>,
528 pub decision: Option<DecisionInfo>,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct ToolCallInfo {
534 pub name: String,
535 pub args: Value,
536 pub result: Option<Value>,
537 pub error: Option<String>,
538 pub execution_time_ms: Option<u64>,
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize)]
543pub struct DecisionInfo {
544 pub turn_number: usize,
545 pub action_type: String,
546 pub description: String,
547 pub reasoning: String,
548 pub outcome: Option<String>,
549 pub confidence_score: Option<f64>,
550 pub timestamp: u64,
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct ErrorInfo {
556 pub error_type: String,
557 pub message: String,
558 pub turn_number: usize,
559 pub recoverable: bool,
560 pub timestamp: u64,
561}
562
563#[derive(Debug, Clone, Serialize, Deserialize)]
565pub struct TaskInfo {
566 pub task_type: String,
567 pub description: String,
568 pub completed: bool,
569 pub success: bool,
570 pub duration_seconds: Option<u64>,
571 pub tools_used: Vec<String>,
572 pub dependencies: Vec<String>,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize)]
577pub struct ProjectSpec {
578 pub name: String,
579 pub features: Vec<String>,
580 pub template: Option<String>,
581 pub dependencies: HashMap<String, String>,
582}
583
584#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct WorkspaceAnalysis {
587 pub root_path: String,
588 pub project_type: Option<String>,
589 pub languages: Vec<String>,
590 pub frameworks: Vec<String>,
591 pub config_files: Vec<String>,
592 pub source_files: Vec<String>,
593 pub test_files: Vec<String>,
594 pub documentation_files: Vec<String>,
595 pub total_files: usize,
596 pub total_size_bytes: u64,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct CommandResult {
602 pub command: String,
603 pub success: bool,
604 pub stdout: String,
605 pub stderr: String,
606 pub exit_code: Option<i32>,
607 pub execution_time_ms: u64,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct FileOperationResult {
613 pub operation: String,
614 pub path: String,
615 pub success: bool,
616 pub details: HashMap<String, Value>,
617 pub error: Option<String>,
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
622pub struct PerformanceMetrics {
623 pub session_duration_seconds: u64,
624 pub total_api_calls: usize,
625 pub total_tokens_used: Option<usize>,
626 pub average_response_time_ms: f64,
627 pub tool_execution_count: usize,
628 pub error_count: usize,
629 pub recovery_success_rate: f64,
630}
631
632#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct QualityMetrics {
635 pub decision_confidence_avg: f64,
636 pub tool_success_rate: f64,
637 pub error_recovery_rate: f64,
638 pub context_preservation_rate: f64,
639 pub user_satisfaction_score: Option<f64>,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct ToolConfig {
645 pub enable_validation: bool,
646 pub max_execution_time_seconds: u64,
647 pub allow_file_creation: bool,
648 pub allow_file_deletion: bool,
649 pub working_directory: Option<String>,
650}
651
652#[derive(Debug, Clone, Serialize, Deserialize)]
654pub struct ContextConfig {
655 pub max_context_length: usize,
656 pub compression_threshold: usize,
657 pub summarization_interval: usize,
658 pub preservation_priority: Vec<String>,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
663pub struct LoggingConfig {
664 pub level: String,
665 pub file_logging: bool,
666 pub log_directory: Option<String>,
667 pub max_log_files: usize,
668 pub max_log_size_mb: usize,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize)]
673pub enum AnalysisDepth {
674 Basic,
675 Standard,
676 Deep,
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize)]
681pub enum OutputFormat {
682 Text,
683 Json,
684 Html,
685}
686
687#[derive(Debug, Clone, Serialize, Deserialize)]
689pub enum CompressionLevel {
690 Light,
691 Medium,
692 Aggressive,
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698
699 #[test]
700 fn test_editing_mode_parse() {
701 assert_eq!(EditingMode::parse("edit"), Some(EditingMode::Edit));
702 assert_eq!(EditingMode::parse("EDIT"), Some(EditingMode::Edit));
703 assert_eq!(EditingMode::parse("Edit"), Some(EditingMode::Edit));
704 assert_eq!(EditingMode::parse("plan"), Some(EditingMode::Plan));
705 assert_eq!(EditingMode::parse("PLAN"), Some(EditingMode::Plan));
706 assert_eq!(EditingMode::parse("Plan"), Some(EditingMode::Plan));
707 assert_eq!(EditingMode::parse("agent"), Some(EditingMode::Agent));
708 assert_eq!(EditingMode::parse("AGENT"), Some(EditingMode::Agent));
709 assert_eq!(EditingMode::parse("Agent"), Some(EditingMode::Agent));
710 assert_eq!(EditingMode::parse("invalid"), None);
711 assert_eq!(EditingMode::parse(""), None);
712 }
713
714 #[test]
715 fn test_editing_mode_as_str() {
716 assert_eq!(EditingMode::Edit.as_str(), "edit");
717 assert_eq!(EditingMode::Plan.as_str(), "plan");
718 assert_eq!(EditingMode::Agent.as_str(), "agent");
719 }
720
721 #[test]
722 fn test_editing_mode_capabilities() {
723 assert!(EditingMode::Edit.can_modify_files());
725 assert!(EditingMode::Edit.can_execute_commands());
726 assert!(!EditingMode::Edit.is_read_only());
727 assert!(!EditingMode::Edit.is_autonomous());
728
729 assert!(!EditingMode::Plan.can_modify_files());
731 assert!(!EditingMode::Plan.can_execute_commands());
732 assert!(EditingMode::Plan.is_read_only());
733 assert!(!EditingMode::Plan.is_autonomous());
734
735 assert!(EditingMode::Agent.can_modify_files());
737 assert!(EditingMode::Agent.can_execute_commands());
738 assert!(!EditingMode::Agent.is_read_only());
739 assert!(EditingMode::Agent.is_autonomous());
740 }
741
742 #[test]
743 fn test_editing_mode_default() {
744 assert_eq!(EditingMode::default(), EditingMode::Edit);
745 }
746
747 #[test]
748 fn test_editing_mode_display() {
749 assert_eq!(format!("{}", EditingMode::Edit), "edit");
750 assert_eq!(format!("{}", EditingMode::Plan), "plan");
751 assert_eq!(format!("{}", EditingMode::Agent), "agent");
752 }
753
754 #[test]
755 fn test_editing_mode_allowed_values() {
756 let values = EditingMode::allowed_values();
757 assert_eq!(values, &["edit", "plan", "agent"]);
758 }
759}