1use crate::event::RiskLevel;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum OperatingMode {
16 Explore,
18 #[default]
20 Execute,
21 Verify,
23 Recover,
25 AskHuman,
27 Sleep,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct GatingProfile {
38 pub allow_side_effects: bool,
40 pub require_approval_for_risk: RiskLevel,
42 pub max_tool_calls_per_tick: u32,
44 pub max_file_mutations_per_tick: u32,
46 pub allow_network: bool,
48 pub allow_shell: bool,
50}
51
52impl Default for GatingProfile {
53 fn default() -> Self {
54 Self {
55 allow_side_effects: true,
56 require_approval_for_risk: RiskLevel::High,
57 max_tool_calls_per_tick: 10,
58 max_file_mutations_per_tick: 5,
59 allow_network: true,
60 allow_shell: true,
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn operating_mode_serde_roundtrip() {
71 for mode in [
72 OperatingMode::Explore,
73 OperatingMode::Execute,
74 OperatingMode::Verify,
75 OperatingMode::Recover,
76 OperatingMode::AskHuman,
77 OperatingMode::Sleep,
78 ] {
79 let json = serde_json::to_string(&mode).unwrap();
80 let back: OperatingMode = serde_json::from_str(&json).unwrap();
81 assert_eq!(mode, back);
82 }
83 }
84
85 #[test]
86 fn gating_profile_default() {
87 let g = GatingProfile::default();
88 assert!(g.allow_side_effects);
89 assert!(g.allow_network);
90 assert!(g.allow_shell);
91 assert_eq!(g.max_tool_calls_per_tick, 10);
92 }
93
94 #[test]
95 fn gating_profile_serde_roundtrip() {
96 let g = GatingProfile::default();
97 let json = serde_json::to_string(&g).unwrap();
98 let back: GatingProfile = serde_json::from_str(&json).unwrap();
99 assert_eq!(back.max_tool_calls_per_tick, g.max_tool_calls_per_tick);
100 }
101}