#[async_trait::async_trait]
pub trait AgentRuntime: Send + Sync {
fn phase(&self) -> &str {
""
}
fn threat_score(&self, _path: &str) -> f32 {
0.0
}
fn sender_trust(&self, _email: &str) -> SenderTrust {
SenderTrust::Unknown
}
fn skill_name(&self) -> &str {
""
}
fn inbox_count(&self) -> usize {
0
}
fn has_otp(&self) -> bool {
false
}
fn has_threat(&self) -> bool {
false
}
fn intent(&self) -> &str {
""
}
fn context_summary(&self) -> String {
let mut parts = Vec::new();
let phase = self.phase();
if !phase.is_empty() {
parts.push(format!("phase={}", phase));
}
if self.inbox_count() > 0 {
parts.push(format!("inbox={}", self.inbox_count()));
}
if self.has_otp() {
parts.push("otp=true".to_string());
}
if self.has_threat() {
parts.push("threat=true".to_string());
}
let skill = self.skill_name();
if !skill.is_empty() {
parts.push(format!("skill={}", skill));
}
parts.join(" | ")
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SenderTrust {
Trusted,
Mismatch,
Unknown,
Blocked,
}
#[derive(Default)]
pub struct SimpleRuntime {
pub phase: String,
pub intent: String,
pub inbox_count: usize,
pub has_otp: bool,
pub has_threat: bool,
pub skill_name: String,
}
#[async_trait::async_trait]
impl AgentRuntime for SimpleRuntime {
fn phase(&self) -> &str {
&self.phase
}
fn skill_name(&self) -> &str {
&self.skill_name
}
fn inbox_count(&self) -> usize {
self.inbox_count
}
fn has_otp(&self) -> bool {
self.has_otp
}
fn has_threat(&self) -> bool {
self.has_threat
}
fn intent(&self) -> &str {
&self.intent
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn context_summary_empty() {
let rt = SimpleRuntime::default();
assert_eq!(rt.context_summary(), "");
}
#[test]
fn context_summary_full() {
let rt = SimpleRuntime {
phase: "Acting".into(),
inbox_count: 3,
has_otp: true,
has_threat: false,
skill_name: "inbox-processing".into(),
intent: "intent_inbox".into(),
};
let s = rt.context_summary();
assert!(s.contains("phase=Acting"));
assert!(s.contains("inbox=3"));
assert!(s.contains("otp=true"));
assert!(s.contains("skill=inbox-processing"));
assert!(!s.contains("threat"));
}
#[test]
fn context_summary_threat_only() {
let rt = SimpleRuntime {
has_threat: true,
..Default::default()
};
let s = rt.context_summary();
assert!(s.contains("threat=true"));
assert!(!s.contains("otp"));
assert!(!s.contains("inbox"));
}
#[test]
fn context_summary_zero_inbox_hidden() {
let rt = SimpleRuntime {
inbox_count: 0,
..Default::default()
};
assert!(!rt.context_summary().contains("inbox"));
}
#[test]
fn sender_trust_defaults_unknown() {
let rt = SimpleRuntime::default();
assert_eq!(rt.sender_trust("anyone@example.com"), SenderTrust::Unknown);
}
#[test]
fn default_runtime_all_safe() {
let rt = SimpleRuntime::default();
assert_eq!(rt.phase(), "");
assert_eq!(rt.inbox_count(), 0);
assert!(!rt.has_otp());
assert!(!rt.has_threat());
assert_eq!(rt.threat_score("any_file"), 0.0);
}
}