1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4pub use crate::scope::ScopeLevel;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "lowercase")]
9pub enum Decision {
10 Allow,
11 Deny,
12 Ask,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub enum DecisionTier {
18 PathPolicy,
20 ExactCache,
22 TokenJaccard,
24 EmbeddingSimilarity,
26 Supervisor,
28 Human,
30 SensitivePath,
32 Override,
34 Default,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct DecisionMetadata {
41 pub tier: DecisionTier,
43
44 pub confidence: f64,
46
47 pub reason: String,
49
50 pub matched_key: Option<CacheKey>,
52
53 pub similarity_score: Option<f64>,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
60pub struct CacheKey {
61 pub sanitized_input: String,
63
64 pub tool: String,
66
67 pub role: String,
69}
70
71impl std::fmt::Display for Decision {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 match self {
74 Decision::Allow => write!(f, "allow"),
75 Decision::Deny => write!(f, "deny"),
76 Decision::Ask => write!(f, "ask"),
77 }
78 }
79}
80
81impl Decision {
82 pub fn precedence(&self) -> u8 {
85 match self {
86 Decision::Deny => 3,
87 Decision::Ask => 2,
88 Decision::Allow => 1,
89 }
90 }
91}
92
93impl std::fmt::Display for ScopeLevel {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 ScopeLevel::Org => write!(f, "org"),
97 ScopeLevel::Project => write!(f, "project"),
98 ScopeLevel::User => write!(f, "user"),
99 ScopeLevel::Role => write!(f, "role"),
100 }
101 }
102}
103
104impl std::str::FromStr for ScopeLevel {
105 type Err = String;
106
107 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
108 match s.to_lowercase().as_str() {
109 "org" => Ok(ScopeLevel::Org),
110 "project" => Ok(ScopeLevel::Project),
111 "user" => Ok(ScopeLevel::User),
112 "role" => Ok(ScopeLevel::Role),
113 _ => Err(format!("unknown scope: {s}")),
114 }
115 }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct DecisionRecord {
121 pub key: CacheKey,
123
124 pub decision: Decision,
126
127 pub metadata: DecisionMetadata,
129
130 pub timestamp: DateTime<Utc>,
132
133 pub scope: ScopeLevel,
135
136 pub file_path: Option<String>,
138
139 pub session_id: String,
141}