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