1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Audit logging core trait and event types
use crate::error::Result;
use crate::guard::GuardDirection;
use crate::tools::permission::ToolPermission;
use chrono::{DateTime, Utc};
use futures::future::BoxFuture;
use serde::{Deserialize, Serialize};
use serde_json::Value;
/// Audit event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
/// Time when the event was recorded.
pub timestamp: DateTime<Utc>,
/// Optional conversation or session identifier.
pub session_id: Option<String>,
/// Agent name associated with the event.
pub agent_name: String,
/// Structured payload describing the event kind.
pub event_type: AuditEventType,
/// Optional distributed tracing identifier.
#[serde(skip_serializing_if = "Option::is_none")]
pub trace_id: Option<String>,
}
impl AuditEvent {
/// Construct a new event stamped with the current UTC time.
pub fn now(session_id: Option<String>, agent_name: String, event_type: AuditEventType) -> Self {
Self {
timestamp: Utc::now(),
session_id,
agent_name,
event_type,
trace_id: None,
}
}
}
/// Audit event types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AuditEventType {
/// Raw user input captured before the agent starts processing.
UserInput {
/// User-provided content.
content: String,
},
/// A model invocation with optional token accounting.
LlmCall {
/// Model name used for the request.
model: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// Prompt token count if the provider reported it.
prompt_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Completion token count if the provider reported it.
completion_tokens: Option<u64>,
},
/// A tool invocation and its observed outcome.
ToolCall {
/// Tool identifier.
tool: String,
/// Serialized tool input.
input: Value,
/// Tool output text.
output: String,
/// Whether the tool succeeded.
success: bool,
/// Execution duration in milliseconds.
duration_ms: u64,
},
/// A guard rejected input or output.
GuardBlock {
/// Guard name.
guard: String,
/// Whether the guard acted on input or output.
direction: GuardDirection,
/// Human-readable reason.
reason: String,
},
/// A permission policy denied a tool invocation.
PermissionDenied {
/// Tool identifier.
tool: String,
/// Permissions that triggered the denial.
required: Vec<ToolPermission>,
/// Human-readable reason.
reason: String,
},
/// Final answer returned to the caller.
FinalAnswer {
/// Final response content.
content: String,
},
/// Approval request was initiated
ApprovalRequested {
/// Tool name
tool: String,
/// Argument hash (for cache matching)
args_hash: String,
/// Risk level
risk_level: String,
},
/// Approval decision was returned
ApprovalCompleted {
/// Tool name
tool: String,
/// Decision result
decision: String,
/// Approval scope
scope: String,
/// Rejection reason (if any)
reason: Option<String>,
/// Duration from request to decision (milliseconds)
duration_ms: u64,
},
}
/// 审计查询过滤器
#[derive(Debug, Default, Clone)]
pub struct AuditFilter {
/// Restrict results to a specific session.
pub session_id: Option<String>,
/// Restrict results to a specific agent name.
pub agent_name: Option<String>,
/// Inclusive lower timestamp bound.
pub from: Option<DateTime<Utc>>,
/// Inclusive upper timestamp bound.
pub to: Option<DateTime<Utc>>,
/// Maximum number of results to return.
pub limit: Option<usize>,
}
/// 审计日志记录器 trait
pub trait AuditLogger: Send + Sync {
/// Persist one audit event.
fn log<'a>(&'a self, event: AuditEvent) -> BoxFuture<'a, Result<()>>;
/// Query stored events with a filter.
fn query<'a>(&'a self, filter: AuditFilter) -> BoxFuture<'a, Result<Vec<AuditEvent>>>;
}