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
use serde::{Deserialize, Serialize};
use crate::auto_approve::types::JudgmentUsage;
use crate::detectors::DetectionReason;
/// Audit event types for detection logging
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event")]
pub enum AuditEvent {
/// Agent status changed
StateChanged {
ts: u64,
pane_id: String,
agent_type: String,
source: String,
prev_status: String,
new_status: String,
reason: DetectionReason,
screen_context: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
prev_state_duration_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
approval_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
approval_details: Option<String>,
},
/// IPC and capture-pane disagree on status
SourceDisagreement {
ts: u64,
pane_id: String,
agent_type: String,
ipc_status: String,
capture_status: String,
capture_reason: DetectionReason,
#[serde(default, skip_serializing_if = "Option::is_none")]
screen_context: Option<String>,
},
/// New agent appeared
AgentAppeared {
ts: u64,
pane_id: String,
agent_type: String,
source: String,
initial_status: String,
},
/// Agent disappeared
AgentDisappeared {
ts: u64,
pane_id: String,
agent_type: String,
last_status: String,
},
/// Auto-approve AI judgment result
AutoApproveJudgment {
ts: u64,
pane_id: String,
agent_type: String,
/// Approval type (e.g., "file_edit", "shell_command")
approval_type: String,
/// Details of the approval request
approval_details: String,
/// Decision: "approve", "reject", "uncertain", or "error"
decision: String,
/// AI reasoning or error message
reasoning: String,
/// Model used for judgment
model: String,
/// Time taken for judgment in milliseconds
elapsed_ms: u64,
/// Whether approval keys were actually sent
approval_sent: bool,
/// Token usage and cost (if available from claude CLI)
#[serde(default, skip_serializing_if = "Option::is_none")]
usage: Option<JudgmentUsage>,
/// Screen context (included for approve/reject decisions)
#[serde(default, skip_serializing_if = "Option::is_none")]
screen_context: Option<String>,
},
/// User sent input while agent was detected as Processing
/// (possible false negative — detection may have missed an approval prompt)
UserInputDuringProcessing {
ts: u64,
pane_id: String,
agent_type: String,
/// What action the user took: "input_text", "passthrough_key"
action: String,
/// Source of input: "tui_input_mode", "tui_passthrough", "web_api_input"
input_source: String,
/// Current detected status at the time of input (always "processing")
current_status: String,
/// Detection reason at the time of input
#[serde(default, skip_serializing_if = "Option::is_none")]
detection_reason: Option<DetectionReason>,
/// Detection source: "ipc_socket" or "capture_pane"
detection_source: String,
/// Last ~20 lines of pane content for post-hoc analysis
#[serde(default, skip_serializing_if = "Option::is_none")]
screen_context: Option<String>,
},
}