1use std::collections::HashMap;
11use std::path::PathBuf;
12
13use serde::{Deserialize, Serialize};
14
15use zeph_common::SkillTrustLevel;
16
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
21#[serde(rename_all = "lowercase")]
22pub enum AutonomyLevel {
23 ReadOnly,
25 #[default]
27 Supervised,
28 Full,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
34#[serde(rename_all = "lowercase")]
35pub enum PermissionAction {
36 Allow,
38 Ask,
40 Deny,
42}
43
44#[derive(Debug, Clone, Deserialize, Serialize)]
46pub struct PermissionRule {
47 pub pattern: String,
49 pub action: PermissionAction,
51}
52
53#[derive(Debug, Clone, Deserialize, Serialize, Default)]
55pub struct PermissionsConfig {
56 #[serde(flatten)]
58 pub tools: HashMap<String, Vec<PermissionRule>>,
59}
60
61fn default_true() -> bool {
64 true
65}
66
67fn default_shell_tools() -> Vec<String> {
68 vec![
69 "bash".to_string(),
70 "shell".to_string(),
71 "terminal".to_string(),
72 ]
73}
74
75fn default_guarded_tools() -> Vec<String> {
76 vec!["fetch".to_string(), "web_scrape".to_string()]
77}
78
79#[derive(Debug, Clone, Deserialize, Serialize)]
81pub struct DestructiveVerifierConfig {
82 #[serde(default = "default_true")]
84 pub enabled: bool,
85 #[serde(default)]
87 pub allowed_paths: Vec<String>,
88 #[serde(default)]
90 pub extra_patterns: Vec<String>,
91 #[serde(default = "default_shell_tools")]
93 pub shell_tools: Vec<String>,
94}
95
96impl Default for DestructiveVerifierConfig {
97 fn default() -> Self {
98 Self {
99 enabled: true,
100 allowed_paths: Vec::new(),
101 extra_patterns: Vec::new(),
102 shell_tools: default_shell_tools(),
103 }
104 }
105}
106
107#[derive(Debug, Clone, Deserialize, Serialize)]
109pub struct InjectionVerifierConfig {
110 #[serde(default = "default_true")]
112 pub enabled: bool,
113 #[serde(default)]
115 pub extra_patterns: Vec<String>,
116 #[serde(default)]
118 pub allowlisted_urls: Vec<String>,
119}
120
121impl Default for InjectionVerifierConfig {
122 fn default() -> Self {
123 Self {
124 enabled: true,
125 extra_patterns: Vec::new(),
126 allowlisted_urls: Vec::new(),
127 }
128 }
129}
130
131#[derive(Debug, Clone, Deserialize, Serialize)]
133pub struct UrlGroundingVerifierConfig {
134 #[serde(default = "default_true")]
136 pub enabled: bool,
137 #[serde(default = "default_guarded_tools")]
139 pub guarded_tools: Vec<String>,
140}
141
142impl Default for UrlGroundingVerifierConfig {
143 fn default() -> Self {
144 Self {
145 enabled: true,
146 guarded_tools: default_guarded_tools(),
147 }
148 }
149}
150
151#[derive(Debug, Clone, Deserialize, Serialize)]
153pub struct FirewallVerifierConfig {
154 #[serde(default = "default_true")]
156 pub enabled: bool,
157 #[serde(default)]
159 pub blocked_paths: Vec<String>,
160 #[serde(default)]
162 pub blocked_env_vars: Vec<String>,
163 #[serde(default)]
165 pub exempt_tools: Vec<String>,
166}
167
168impl Default for FirewallVerifierConfig {
169 fn default() -> Self {
170 Self {
171 enabled: true,
172 blocked_paths: Vec::new(),
173 blocked_env_vars: Vec::new(),
174 exempt_tools: Vec::new(),
175 }
176 }
177}
178
179#[derive(Debug, Clone, Deserialize, Serialize)]
181pub struct PreExecutionVerifierConfig {
182 #[serde(default = "default_true")]
184 pub enabled: bool,
185 #[serde(default)]
187 pub destructive_commands: DestructiveVerifierConfig,
188 #[serde(default)]
190 pub injection_patterns: InjectionVerifierConfig,
191 #[serde(default)]
193 pub url_grounding: UrlGroundingVerifierConfig,
194 #[serde(default)]
196 pub firewall: FirewallVerifierConfig,
197}
198
199impl Default for PreExecutionVerifierConfig {
200 fn default() -> Self {
201 Self {
202 enabled: true,
203 destructive_commands: DestructiveVerifierConfig::default(),
204 injection_patterns: InjectionVerifierConfig::default(),
205 url_grounding: UrlGroundingVerifierConfig::default(),
206 firewall: FirewallVerifierConfig::default(),
207 }
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
215#[serde(rename_all = "snake_case")]
216pub enum PolicyEffect {
217 Allow,
219 Deny,
221}
222
223#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
225#[serde(rename_all = "lowercase")]
226pub enum DefaultEffect {
227 Allow,
229 #[default]
231 Deny,
232}
233
234fn default_deny() -> DefaultEffect {
235 DefaultEffect::Deny
236}
237
238#[derive(Debug, Clone, Deserialize, Serialize, Default)]
240pub struct PolicyConfig {
241 #[serde(default)]
243 pub enabled: bool,
244 #[serde(default = "default_deny")]
246 pub default_effect: DefaultEffect,
247 #[serde(default)]
249 pub rules: Vec<PolicyRuleConfig>,
250 pub policy_file: Option<String>,
252}
253
254#[derive(Debug, Clone, Deserialize, Serialize)]
256pub struct PolicyRuleConfig {
257 pub effect: PolicyEffect,
259 pub tool: String,
261 #[serde(default)]
263 pub paths: Vec<String>,
264 #[serde(default)]
266 pub env: Vec<String>,
267 pub trust_level: Option<SkillTrustLevel>,
269 pub args_match: Option<String>,
271 #[serde(default)]
273 pub capabilities: Vec<String>,
274}
275
276#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
280#[serde(rename_all = "kebab-case")]
281pub enum SandboxProfile {
282 ReadOnly,
284 #[default]
286 Workspace,
287 #[serde(rename = "network-allow-all", alias = "network")]
289 NetworkAllowAll,
290 Off,
292}
293
294fn default_sandbox_profile() -> SandboxProfile {
295 SandboxProfile::Workspace
296}
297
298fn default_sandbox_backend() -> String {
299 "auto".into()
300}
301
302#[derive(Debug, Clone, Deserialize, Serialize)]
304pub struct SandboxConfig {
305 #[serde(default)]
307 pub enabled: bool,
308 #[serde(default = "default_sandbox_profile")]
310 pub profile: SandboxProfile,
311 #[serde(default)]
313 pub allow_read: Vec<PathBuf>,
314 #[serde(default)]
316 pub allow_write: Vec<PathBuf>,
317 #[serde(default = "default_true")]
319 pub strict: bool,
320 #[serde(default = "default_sandbox_backend")]
322 pub backend: String,
323 #[serde(default)]
325 pub denied_domains: Vec<String>,
326 #[serde(default)]
328 pub fail_if_unavailable: bool,
329}
330
331impl Default for SandboxConfig {
332 fn default() -> Self {
333 Self {
334 enabled: false,
335 profile: default_sandbox_profile(),
336 allow_read: Vec::new(),
337 allow_write: Vec::new(),
338 strict: true,
339 backend: default_sandbox_backend(),
340 denied_domains: Vec::new(),
341 fail_if_unavailable: false,
342 }
343 }
344}
345
346#[derive(Debug, Clone, Deserialize, Serialize)]
350pub struct SecurityFilterConfig {
351 #[serde(default = "default_true")]
353 pub enabled: bool,
354 #[serde(default)]
356 pub extra_patterns: Vec<String>,
357}
358
359impl Default for SecurityFilterConfig {
360 fn default() -> Self {
361 Self {
362 enabled: true,
363 extra_patterns: Vec::new(),
364 }
365 }
366}
367
368#[derive(Debug, Clone, Deserialize, Serialize)]
370pub struct FilterConfig {
371 #[serde(default = "default_true")]
373 pub enabled: bool,
374 #[serde(default)]
376 pub security: SecurityFilterConfig,
377 #[serde(default, skip_serializing_if = "Option::is_none")]
379 pub filters_path: Option<PathBuf>,
380}
381
382impl Default for FilterConfig {
383 fn default() -> Self {
384 Self {
385 enabled: true,
386 security: SecurityFilterConfig::default(),
387 filters_path: None,
388 }
389 }
390}
391
392fn default_overflow_threshold() -> usize {
395 50_000
396}
397
398fn default_retention_days() -> u64 {
399 7
400}
401
402fn default_max_overflow_bytes() -> usize {
403 10 * 1024 * 1024
404}
405
406#[derive(Debug, Clone, Deserialize, Serialize)]
408pub struct OverflowConfig {
409 #[serde(default = "default_overflow_threshold")]
411 pub threshold: usize,
412 #[serde(default = "default_retention_days")]
414 pub retention_days: u64,
415 #[serde(default = "default_max_overflow_bytes")]
417 pub max_overflow_bytes: usize,
418}
419
420impl Default for OverflowConfig {
421 fn default() -> Self {
422 Self {
423 threshold: default_overflow_threshold(),
424 retention_days: default_retention_days(),
425 max_overflow_bytes: default_max_overflow_bytes(),
426 }
427 }
428}
429
430fn default_anomaly_window() -> usize {
431 10
432}
433
434fn default_anomaly_error_threshold() -> f64 {
435 0.5
436}
437
438fn default_anomaly_critical_threshold() -> f64 {
439 0.8
440}
441
442#[derive(Debug, Clone, Deserialize, Serialize)]
444pub struct AnomalyConfig {
445 #[serde(default = "default_true")]
447 pub enabled: bool,
448 #[serde(default = "default_anomaly_window")]
450 pub window_size: usize,
451 #[serde(default = "default_anomaly_error_threshold")]
453 pub error_threshold: f64,
454 #[serde(default = "default_anomaly_critical_threshold")]
456 pub critical_threshold: f64,
457 #[serde(default = "default_true")]
459 pub reasoning_model_warning: bool,
460}
461
462impl Default for AnomalyConfig {
463 fn default() -> Self {
464 Self {
465 enabled: true,
466 window_size: default_anomaly_window(),
467 error_threshold: default_anomaly_error_threshold(),
468 critical_threshold: default_anomaly_critical_threshold(),
469 reasoning_model_warning: true,
470 }
471 }
472}
473
474fn default_cache_ttl_secs() -> u64 {
475 300
476}
477
478#[derive(Debug, Clone, Deserialize, Serialize)]
480pub struct ResultCacheConfig {
481 #[serde(default = "default_true")]
483 pub enabled: bool,
484 #[serde(default = "default_cache_ttl_secs")]
486 pub ttl_secs: u64,
487}
488
489impl Default for ResultCacheConfig {
490 fn default() -> Self {
491 Self {
492 enabled: true,
493 ttl_secs: default_cache_ttl_secs(),
494 }
495 }
496}
497
498fn default_tafc_complexity_threshold() -> f64 {
499 0.6
500}
501
502#[derive(Debug, Clone, Deserialize, Serialize)]
504pub struct TafcConfig {
505 #[serde(default)]
507 pub enabled: bool,
508 #[serde(default = "default_tafc_complexity_threshold")]
510 pub complexity_threshold: f64,
511}
512
513impl Default for TafcConfig {
514 fn default() -> Self {
515 Self {
516 enabled: false,
517 complexity_threshold: default_tafc_complexity_threshold(),
518 }
519 }
520}
521
522impl TafcConfig {
523 #[must_use]
525 pub fn validated(mut self) -> Self {
526 if self.complexity_threshold.is_finite() {
527 self.complexity_threshold = self.complexity_threshold.clamp(0.0, 1.0);
528 } else {
529 self.complexity_threshold = 0.6;
530 }
531 self
532 }
533}
534
535fn default_utility_exempt_tools() -> Vec<String> {
536 vec!["invoke_skill".to_string(), "load_skill".to_string()]
537}
538
539fn default_utility_threshold() -> f32 {
540 0.1
541}
542
543fn default_utility_gain_weight() -> f32 {
544 1.0
545}
546
547fn default_utility_cost_weight() -> f32 {
548 0.5
549}
550
551fn default_utility_redundancy_weight() -> f32 {
552 0.3
553}
554
555fn default_utility_uncertainty_bonus() -> f32 {
556 0.2
557}
558
559#[derive(Debug, Clone, Deserialize, Serialize)]
561#[serde(default)]
562pub struct UtilityScoringConfig {
563 pub enabled: bool,
565 #[serde(default = "default_utility_threshold")]
567 pub threshold: f32,
568 #[serde(default = "default_utility_gain_weight")]
570 pub gain_weight: f32,
571 #[serde(default = "default_utility_cost_weight")]
573 pub cost_weight: f32,
574 #[serde(default = "default_utility_redundancy_weight")]
576 pub redundancy_weight: f32,
577 #[serde(default = "default_utility_uncertainty_bonus")]
579 pub uncertainty_bonus: f32,
580 #[serde(default = "default_utility_exempt_tools")]
582 pub exempt_tools: Vec<String>,
583}
584
585impl Default for UtilityScoringConfig {
586 fn default() -> Self {
587 Self {
588 enabled: false,
589 threshold: default_utility_threshold(),
590 gain_weight: default_utility_gain_weight(),
591 cost_weight: default_utility_cost_weight(),
592 redundancy_weight: default_utility_redundancy_weight(),
593 uncertainty_bonus: default_utility_uncertainty_bonus(),
594 exempt_tools: default_utility_exempt_tools(),
595 }
596 }
597}
598
599impl UtilityScoringConfig {
600 pub fn validate(&self) -> Result<(), String> {
606 let fields = [
607 ("threshold", self.threshold),
608 ("gain_weight", self.gain_weight),
609 ("cost_weight", self.cost_weight),
610 ("redundancy_weight", self.redundancy_weight),
611 ("uncertainty_bonus", self.uncertainty_bonus),
612 ];
613 for (name, val) in fields {
614 if !val.is_finite() {
615 return Err(format!("[tools.utility] {name} must be finite, got {val}"));
616 }
617 if val < 0.0 {
618 return Err(format!("[tools.utility] {name} must be >= 0, got {val}"));
619 }
620 }
621 Ok(())
622 }
623}
624
625#[derive(Debug, Clone, Default, Deserialize, Serialize)]
627pub struct ToolDependency {
628 #[serde(default, skip_serializing_if = "Vec::is_empty")]
630 pub requires: Vec<String>,
631 #[serde(default, skip_serializing_if = "Vec::is_empty")]
633 pub prefers: Vec<String>,
634}
635
636fn default_boost_per_dep() -> f32 {
637 0.15
638}
639
640fn default_max_total_boost() -> f32 {
641 0.2
642}
643
644#[derive(Debug, Clone, Deserialize, Serialize)]
646pub struct DependencyConfig {
647 #[serde(default)]
649 pub enabled: bool,
650 #[serde(default = "default_boost_per_dep")]
652 pub boost_per_dep: f32,
653 #[serde(default = "default_max_total_boost")]
655 pub max_total_boost: f32,
656 #[serde(default)]
658 pub rules: HashMap<String, ToolDependency>,
659}
660
661impl Default for DependencyConfig {
662 fn default() -> Self {
663 Self {
664 enabled: false,
665 boost_per_dep: default_boost_per_dep(),
666 max_total_boost: default_max_total_boost(),
667 rules: HashMap::new(),
668 }
669 }
670}
671
672fn default_retry_max_attempts() -> usize {
673 2
674}
675
676fn default_retry_base_ms() -> u64 {
677 500
678}
679
680fn default_retry_max_ms() -> u64 {
681 5_000
682}
683
684fn default_retry_budget_secs() -> u64 {
685 30
686}
687
688#[derive(Debug, Clone, Deserialize, Serialize)]
690pub struct RetryConfig {
691 #[serde(default = "default_retry_max_attempts")]
693 pub max_attempts: usize,
694 #[serde(default = "default_retry_base_ms")]
696 pub base_ms: u64,
697 #[serde(default = "default_retry_max_ms")]
699 pub max_ms: u64,
700 #[serde(default = "default_retry_budget_secs")]
702 pub budget_secs: u64,
703 #[serde(default)]
706 pub parameter_reformat_provider: String,
707}
708
709impl Default for RetryConfig {
710 fn default() -> Self {
711 Self {
712 max_attempts: default_retry_max_attempts(),
713 base_ms: default_retry_base_ms(),
714 max_ms: default_retry_max_ms(),
715 budget_secs: default_retry_budget_secs(),
716 parameter_reformat_provider: String::new(),
717 }
718 }
719}
720
721fn default_adversarial_timeout_ms() -> u64 {
722 3_000
723}
724
725#[derive(Debug, Clone, Deserialize, Serialize)]
727pub struct AdversarialPolicyConfig {
728 #[serde(default)]
730 pub enabled: bool,
731 #[serde(default)]
733 pub policy_provider: String,
734 pub policy_file: Option<String>,
736 #[serde(default)]
738 pub fail_open: bool,
739 #[serde(default = "default_adversarial_timeout_ms")]
741 pub timeout_ms: u64,
742 #[serde(default = "AdversarialPolicyConfig::default_exempt_tools")]
744 pub exempt_tools: Vec<String>,
745}
746
747impl Default for AdversarialPolicyConfig {
748 fn default() -> Self {
749 Self {
750 enabled: false,
751 policy_provider: String::new(),
752 policy_file: None,
753 fail_open: false,
754 timeout_ms: default_adversarial_timeout_ms(),
755 exempt_tools: Self::default_exempt_tools(),
756 }
757 }
758}
759
760impl AdversarialPolicyConfig {
761 #[must_use]
762 pub fn default_exempt_tools() -> Vec<String> {
763 vec![
764 "memory_save".into(),
765 "memory_search".into(),
766 "read_overflow".into(),
767 "load_skill".into(),
768 "invoke_skill".into(),
769 "schedule_deferred".into(),
770 ]
771 }
772}
773
774#[derive(Debug, Clone, Default, Deserialize, Serialize)]
779pub struct FileConfig {
780 #[serde(default)]
782 pub deny_read: Vec<String>,
783 #[serde(default)]
785 pub allow_read: Vec<String>,
786}
787
788#[derive(Debug, Clone, Default, Deserialize, Serialize)]
790pub struct AuthorizationConfig {
791 #[serde(default)]
793 pub enabled: bool,
794 #[serde(default)]
796 pub rules: Vec<PolicyRuleConfig>,
797}
798
799#[derive(Debug, Deserialize, Serialize)]
801pub struct AuditConfig {
802 #[serde(default = "default_true")]
804 pub enabled: bool,
805 #[serde(default = "default_audit_destination")]
807 pub destination: String,
808 #[serde(default)]
810 pub tool_risk_summary: bool,
811}
812
813fn default_audit_destination() -> String {
814 "stdout".into()
815}
816
817impl Default for AuditConfig {
818 fn default() -> Self {
819 Self {
820 enabled: true,
821 destination: default_audit_destination(),
822 tool_risk_summary: false,
823 }
824 }
825}
826
827fn default_timeout() -> u64 {
828 30
829}
830
831fn default_confirm_patterns() -> Vec<String> {
832 vec![
833 "rm ".into(),
834 "git push -f".into(),
835 "git push --force".into(),
836 "drop table".into(),
837 "drop database".into(),
838 "truncate ".into(),
839 "$(".into(),
840 "`".into(),
841 "<(".into(),
842 ">(".into(),
843 "<<<".into(),
844 "eval ".into(),
845 ]
846}
847
848fn default_max_background_runs() -> usize {
849 8
850}
851
852fn default_background_timeout_secs() -> u64 {
853 1800
854}
855
856#[derive(Debug, Deserialize, Serialize)]
858#[allow(clippy::struct_excessive_bools)]
859pub struct ShellConfig {
860 #[serde(default = "default_timeout")]
862 pub timeout: u64,
863 #[serde(default)]
865 pub blocked_commands: Vec<String>,
866 #[serde(default)]
868 pub allowed_commands: Vec<String>,
869 #[serde(default)]
871 pub allowed_paths: Vec<String>,
872 #[serde(default = "default_true")]
874 pub allow_network: bool,
875 #[serde(default = "default_confirm_patterns")]
877 pub confirm_patterns: Vec<String>,
878 #[serde(default = "ShellConfig::default_env_blocklist")]
880 pub env_blocklist: Vec<String>,
881 #[serde(default)]
883 pub transactional: bool,
884 #[serde(default)]
886 pub transaction_scope: Vec<String>,
887 #[serde(default)]
889 pub auto_rollback: bool,
890 #[serde(default)]
892 pub auto_rollback_exit_codes: Vec<i32>,
893 #[serde(default)]
895 pub snapshot_required: bool,
896 #[serde(default)]
898 pub max_snapshot_bytes: u64,
899 #[serde(default = "default_max_background_runs")]
901 pub max_background_runs: usize,
902 #[serde(default = "default_background_timeout_secs")]
904 pub background_timeout_secs: u64,
905}
906
907impl Default for ShellConfig {
908 fn default() -> Self {
909 Self {
910 timeout: default_timeout(),
911 blocked_commands: Vec::new(),
912 allowed_commands: Vec::new(),
913 allowed_paths: Vec::new(),
914 allow_network: true,
915 confirm_patterns: default_confirm_patterns(),
916 env_blocklist: Self::default_env_blocklist(),
917 transactional: false,
918 transaction_scope: Vec::new(),
919 auto_rollback: false,
920 auto_rollback_exit_codes: Vec::new(),
921 snapshot_required: false,
922 max_snapshot_bytes: 0,
923 max_background_runs: default_max_background_runs(),
924 background_timeout_secs: default_background_timeout_secs(),
925 }
926 }
927}
928
929impl ShellConfig {
930 #[must_use]
932 pub fn default_env_blocklist() -> Vec<String> {
933 vec![
934 "ZEPH_".into(),
935 "AWS_".into(),
936 "AZURE_".into(),
937 "GCP_".into(),
938 "GOOGLE_".into(),
939 "OPENAI_".into(),
940 "ANTHROPIC_".into(),
941 "HF_".into(),
942 "HUGGING".into(),
943 ]
944 }
945}
946
947fn default_scrape_timeout() -> u64 {
948 15
949}
950
951fn default_max_body_bytes() -> usize {
952 4_194_304
953}
954
955#[derive(Debug, Deserialize, Serialize)]
957pub struct ScrapeConfig {
958 #[serde(default = "default_scrape_timeout")]
960 pub timeout: u64,
961 #[serde(default = "default_max_body_bytes")]
963 pub max_body_bytes: usize,
964 #[serde(default)]
966 pub allowed_domains: Vec<String>,
967 #[serde(default)]
969 pub denied_domains: Vec<String>,
970}
971
972impl Default for ScrapeConfig {
973 fn default() -> Self {
974 Self {
975 timeout: default_scrape_timeout(),
976 max_body_bytes: default_max_body_bytes(),
977 allowed_domains: Vec::new(),
978 denied_domains: Vec::new(),
979 }
980 }
981}
982
983#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
985#[serde(rename_all = "kebab-case")]
986pub enum SpeculationMode {
987 #[default]
989 Off,
990 Decoding,
992 Pattern,
994 Both,
996}
997
998#[derive(Debug, Clone, Deserialize, Serialize)]
1000pub struct SpeculativePatternConfig {
1001 #[serde(default)]
1003 pub enabled: bool,
1004 #[serde(default = "default_min_observations")]
1006 pub min_observations: u32,
1007 #[serde(default = "default_half_life_days")]
1009 pub half_life_days: f64,
1010 #[serde(default)]
1012 pub rerank_provider: String,
1013}
1014
1015fn default_min_observations() -> u32 {
1016 5
1017}
1018
1019fn default_half_life_days() -> f64 {
1020 14.0
1021}
1022
1023impl Default for SpeculativePatternConfig {
1024 fn default() -> Self {
1025 Self {
1026 enabled: false,
1027 min_observations: default_min_observations(),
1028 half_life_days: default_half_life_days(),
1029 rerank_provider: String::new(),
1030 }
1031 }
1032}
1033
1034#[derive(Debug, Clone, Default, Deserialize, Serialize)]
1036pub struct SpeculativeAllowlistConfig {
1037 #[serde(default)]
1039 pub shell: Vec<String>,
1040}
1041
1042fn default_max_in_flight() -> usize {
1043 4
1044}
1045
1046fn default_confidence_threshold() -> f32 {
1047 0.55
1048}
1049
1050fn default_max_wasted_per_minute() -> u64 {
1051 100
1052}
1053
1054fn default_ttl_seconds() -> u64 {
1055 30
1056}
1057
1058#[derive(Debug, Clone, Deserialize, Serialize)]
1060pub struct SpeculativeConfig {
1061 #[serde(default)]
1063 pub mode: SpeculationMode,
1064 #[serde(default = "default_max_in_flight")]
1066 pub max_in_flight: usize,
1067 #[serde(default = "default_confidence_threshold")]
1069 pub confidence_threshold: f32,
1070 #[serde(default = "default_max_wasted_per_minute")]
1072 pub max_wasted_per_minute: u64,
1073 #[serde(default = "default_ttl_seconds")]
1075 pub ttl_seconds: u64,
1076 #[serde(default = "default_true")]
1078 pub audit: bool,
1079 #[serde(default)]
1081 pub pattern: SpeculativePatternConfig,
1082 #[serde(default)]
1084 pub allowlist: SpeculativeAllowlistConfig,
1085}
1086
1087impl Default for SpeculativeConfig {
1088 fn default() -> Self {
1089 Self {
1090 mode: SpeculationMode::Off,
1091 max_in_flight: default_max_in_flight(),
1092 confidence_threshold: default_confidence_threshold(),
1093 max_wasted_per_minute: default_max_wasted_per_minute(),
1094 ttl_seconds: default_ttl_seconds(),
1095 audit: true,
1096 pattern: SpeculativePatternConfig::default(),
1097 allowlist: SpeculativeAllowlistConfig::default(),
1098 }
1099 }
1100}
1101
1102#[derive(Debug, Clone, Deserialize, Serialize)]
1104#[serde(default)]
1105#[allow(clippy::struct_excessive_bools)]
1106pub struct EgressConfig {
1107 pub enabled: bool,
1109 pub log_blocked: bool,
1111 pub log_response_bytes: bool,
1113 pub log_hosts_to_tui: bool,
1115}
1116
1117impl Default for EgressConfig {
1118 fn default() -> Self {
1119 Self {
1120 enabled: true,
1121 log_blocked: true,
1122 log_response_bytes: true,
1123 log_hosts_to_tui: true,
1124 }
1125 }
1126}
1127
1128#[derive(Debug, Deserialize, Serialize)]
1136pub struct ToolsConfig {
1137 #[serde(default = "default_true")]
1139 pub enabled: bool,
1140 #[serde(default = "default_true")]
1142 pub summarize_output: bool,
1143 #[serde(default)]
1145 pub shell: ShellConfig,
1146 #[serde(default)]
1148 pub scrape: ScrapeConfig,
1149 #[serde(default)]
1151 pub audit: AuditConfig,
1152 #[serde(default)]
1154 pub permissions: Option<PermissionsConfig>,
1155 #[serde(default)]
1157 pub filters: FilterConfig,
1158 #[serde(default)]
1160 pub overflow: OverflowConfig,
1161 #[serde(default)]
1163 pub anomaly: AnomalyConfig,
1164 #[serde(default)]
1166 pub result_cache: ResultCacheConfig,
1167 #[serde(default)]
1169 pub tafc: TafcConfig,
1170 #[serde(default)]
1172 pub dependencies: DependencyConfig,
1173 #[serde(default)]
1175 pub retry: RetryConfig,
1176 #[serde(default)]
1178 pub policy: PolicyConfig,
1179 #[serde(default)]
1181 pub adversarial_policy: AdversarialPolicyConfig,
1182 #[serde(default)]
1184 pub utility: UtilityScoringConfig,
1185 #[serde(default)]
1187 pub file: FileConfig,
1188 #[serde(default)]
1190 pub authorization: AuthorizationConfig,
1191 #[serde(default)]
1193 pub max_tool_calls_per_session: Option<u32>,
1194 #[serde(default)]
1196 pub speculative: SpeculativeConfig,
1197 #[serde(default)]
1199 pub sandbox: SandboxConfig,
1200 #[serde(default)]
1202 pub egress: EgressConfig,
1203}
1204
1205impl Default for ToolsConfig {
1206 fn default() -> Self {
1207 Self {
1208 enabled: true,
1209 summarize_output: true,
1210 shell: ShellConfig::default(),
1211 scrape: ScrapeConfig::default(),
1212 audit: AuditConfig::default(),
1213 permissions: None,
1214 filters: FilterConfig::default(),
1215 overflow: OverflowConfig::default(),
1216 anomaly: AnomalyConfig::default(),
1217 result_cache: ResultCacheConfig::default(),
1218 tafc: TafcConfig::default(),
1219 dependencies: DependencyConfig::default(),
1220 retry: RetryConfig::default(),
1221 policy: PolicyConfig::default(),
1222 adversarial_policy: AdversarialPolicyConfig::default(),
1223 utility: UtilityScoringConfig::default(),
1224 file: FileConfig::default(),
1225 authorization: AuthorizationConfig::default(),
1226 max_tool_calls_per_session: None,
1227 speculative: SpeculativeConfig::default(),
1228 sandbox: SandboxConfig::default(),
1229 egress: EgressConfig::default(),
1230 }
1231 }
1232}
1233
1234#[cfg(test)]
1235mod tests {
1236 use super::*;
1237
1238 #[test]
1239 fn deserialize_default_config() {
1240 let toml_str = r#"
1241 enabled = true
1242
1243 [shell]
1244 timeout = 60
1245 blocked_commands = ["rm -rf /", "sudo"]
1246 "#;
1247
1248 let config: ToolsConfig = toml::from_str(toml_str).unwrap();
1249 assert!(config.enabled);
1250 assert_eq!(config.shell.timeout, 60);
1251 assert_eq!(config.shell.blocked_commands.len(), 2);
1252 }
1253
1254 #[test]
1255 fn empty_blocked_commands() {
1256 let config: ToolsConfig = toml::from_str(r"[shell]\ntimeout = 30\n").unwrap_or_default();
1257 assert!(config.enabled);
1258 }
1259
1260 #[test]
1261 fn default_tools_config() {
1262 let config = ToolsConfig::default();
1263 assert!(config.enabled);
1264 assert!(config.summarize_output);
1265 assert_eq!(config.shell.timeout, 30);
1266 assert!(config.shell.blocked_commands.is_empty());
1267 assert!(config.audit.enabled);
1268 }
1269}