use std::time::{Duration, Instant};
use serde_json::Value;
use crate::permission::Scope;
use crate::tool::ToolResult;
use crate::types::StopReason;
#[derive(Debug, Clone)]
pub enum AgentEvent {
RunStarted {
input: String,
timestamp: Instant,
},
RunCompleted {
output: String,
duration: Duration,
},
RunFailed {
error: String,
duration: Duration,
},
ModelCallStarted {
message_count: usize,
tool_count: usize,
timestamp: Instant,
},
ModelCallStreaming {
delta: String,
accumulated_length: usize,
},
ModelCallCompleted {
response_content: String,
tokens: Option<TokenUsage>,
duration: Duration,
stop_reason: Option<StopReason>,
},
ToolRequested {
tool_use_id: String,
name: String,
input: Value,
},
ToolExecuting {
tool_use_id: String,
name: String,
},
ToolCompleted {
tool_use_id: String,
name: String,
output: ToolResult,
duration: Duration,
},
ToolFailed {
tool_use_id: String,
name: String,
error: String,
duration: Duration,
},
PermissionRequired {
proposal_id: String,
tool_name: String,
params: Value,
params_hash: String,
},
PermissionGranted {
tool_use_id: String,
tool_name: String,
scope: Option<Scope>,
},
PermissionDenied {
tool_use_id: String,
tool_name: String,
reason: String,
},
#[cfg(feature = "session")]
SessionResumed {
session_id: String,
message_count: usize,
created_at: chrono::DateTime<chrono::Utc>,
},
#[cfg(feature = "session")]
SessionSaved {
session_id: String,
message_count: usize,
},
}
#[derive(Debug, Clone, Copy)]
pub struct TokenUsage {
pub input_tokens: usize,
pub output_tokens: usize,
}
impl TokenUsage {
pub fn total(&self) -> usize {
self.input_tokens + self.output_tokens
}
}
pub trait AgentHook: Send + Sync {
fn on_event(&self, event: &AgentEvent);
}
impl<F> AgentHook for F
where
F: Fn(&AgentEvent) + Send + Sync,
{
fn on_event(&self, event: &AgentEvent) {
self(event)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HookId(pub(crate) u64);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_usage_total() {
let cases = [
(100, 50, 150),
(0, 0, 0),
(1000, 2000, 3000),
(1, 0, 1),
(0, 1, 1),
(usize::MAX / 2, usize::MAX / 2, usize::MAX - 1),
];
for (input, output, expected) in cases {
let usage = TokenUsage {
input_tokens: input,
output_tokens: output,
};
assert_eq!(
usage.total(),
expected,
"Failed for input={}, output={}",
input,
output
);
}
}
}