1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use super::*;
5impl Command {
6 pub fn kind(&self) -> &'static str {
9 match self {
10 Self::Ping => "ping",
11 Self::Metrics => "metrics",
12 Self::Get(_) => "get",
13 Self::HookEvaluate(_) => "hook_evaluate",
14 Self::PolicyEvaluate(_) => "policy_evaluate",
15 Self::ScanPrefix(_) => "scan_prefix",
16 Self::ScanKeys(_) => "scan_keys",
17 Self::History(_) => "history",
18 Self::HistorySince(_) => "history_since",
19 Self::SessionCheckConsulted(_) => "session_check_consulted",
20 Self::SessionCheckConsultedRecent(_) => "session_check_consulted_recent",
21 Self::MemQuery(_) => "mem_query",
22 Self::ScanEnforcementEvents(_) => "scan_enforcement_events",
23 Self::ScanEnforcementEventsWithSkips(_) => "scan_enforcement_events_with_skips",
24 Self::ScanEnforcementEventsSinceMs(_) => "scan_enforcement_events_since_ms",
25 Self::ConfigGet(_) => "config_get",
26 Self::ConfigSet(_) => "config_set",
27 Self::SandboxAudit(_) => "sandbox_audit",
28 Self::MemGet(_) => "mem_get",
29 Self::MemBootstrap(_) => "mem_bootstrap",
30 Self::GotchaUpsert(_) => "gotcha_upsert",
31 Self::GotchaConfirm(_) => "gotcha_confirm",
32 Self::GotchaTombstone(_) => "gotcha_tombstone",
33 Self::PolicyWrite(_) => "policy_write",
34 Self::FileEnrich(_) => "file_enrich",
35 Self::FileReparse(_) => "file_reparse",
36 Self::FileEditHook(_) => "file_edit_hook",
37 Self::DocCapture(_) => "doc_capture",
38 Self::DecisionUpsert(_) => "decision_upsert",
39 Self::DevNoteUpsert(_) => "dev_note_upsert",
40 Self::SessionLog(_) => "session_log",
41 Self::InstructionsLoaded(_) => "instructions_loaded",
42 Self::ConsultationHit(_) => "consultation_hit",
43 Self::PolicyShadowObserve(_) => "policy_shadow_observe",
44 Self::SessionFlush => "session_flush",
45 Self::SessionHarvest => "session_harvest",
46 Self::SessionClearConsults => "session_clear_consults",
47 Self::SubagentHarvest(_) => "subagent_harvest",
48 Self::SubagentSpawned(_) => "subagent_spawned",
49 Self::SubagentEdge(_) => "subagent_edge",
50 Self::RecordImport(_) => "record_import",
51 }
52 }
53
54 pub fn target_key(&self) -> &str {
57 match self {
58 Self::Get(i) => &i.key,
59 Self::HookEvaluate(i) => &i.file_key,
60 Self::PolicyEvaluate(_) => "",
61 Self::ScanPrefix(i) => &i.prefix,
62 Self::ScanKeys(i) => &i.prefix,
63 Self::History(i) => &i.key,
64 Self::HistorySince(i) => &i.key,
65 Self::SessionCheckConsulted(i) => &i.key,
66 Self::SessionCheckConsultedRecent(i) => &i.key,
67 Self::MemQuery(i) => &i.query,
68 Self::MemGet(i) => &i.key,
69 Self::GotchaUpsert(i) => &i.key,
70 Self::GotchaConfirm(i) => &i.key,
71 Self::GotchaTombstone(i) => &i.key,
72 Self::PolicyWrite(i) => &i.key,
73 Self::FileEnrich(i) => &i.path,
74 Self::FileReparse(i) => &i.path,
75 Self::FileEditHook(i) => &i.path,
76 Self::DocCapture(i) => &i.path,
77 Self::DecisionUpsert(i) => &i.slug,
78 Self::DevNoteUpsert(i) => i.key.as_deref().unwrap_or(""),
79 Self::SessionLog(i) => &i.key,
80 Self::InstructionsLoaded(i) => &i.payload.file_path,
81 Self::ConsultationHit(i) => &i.key,
82 Self::PolicyShadowObserve(i) => &i.policy_key,
83 Self::ConfigGet(i) => &i.key,
84 Self::ConfigSet(i) => &i.key,
85 Self::SandboxAudit(i) => &i.setting,
86 Self::Ping
87 | Self::Metrics
88 | Self::MemBootstrap(_)
89 | Self::ScanEnforcementEvents(_)
90 | Self::ScanEnforcementEventsWithSkips(_)
91 | Self::ScanEnforcementEventsSinceMs(_)
92 | Self::SessionFlush
93 | Self::SessionHarvest
94 | Self::SessionClearConsults
95 | Self::SubagentHarvest(_)
96 | Self::SubagentSpawned(_)
97 | Self::SubagentEdge(_)
98 | Self::RecordImport(_) => "",
99 }
100 }
101
102 pub fn is_mutation(&self) -> bool {
109 matches!(
110 self,
111 Self::MemGet(_)
113 | Self::MemBootstrap(_)
114 | Self::GotchaUpsert(_)
116 | Self::GotchaConfirm(_)
117 | Self::GotchaTombstone(_)
118 | Self::PolicyWrite(_)
119 | Self::FileEnrich(_)
120 | Self::FileReparse(_)
121 | Self::FileEditHook(_)
122 | Self::DocCapture(_)
123 | Self::DecisionUpsert(_)
124 | Self::DevNoteUpsert(_)
125 | Self::SessionLog(_)
126 | Self::InstructionsLoaded(_)
127 | Self::ConsultationHit(_)
128 | Self::PolicyShadowObserve(_)
129 | Self::ConfigSet(_)
130 | Self::SandboxAudit(_)
131 | Self::SessionFlush
132 | Self::SessionHarvest
133 | Self::SessionClearConsults
134 | Self::SubagentHarvest(_)
135 | Self::SubagentSpawned(_)
136 | Self::SubagentEdge(_)
137 | Self::RecordImport(_)
138 )
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct AuditEntry {
154 pub ts: u64,
156 pub peer_uid: u32,
158 pub peer_pid: Option<u32>,
160 pub daemon_session: Uuid,
162 pub request_id: Uuid,
164 pub command_kind: String,
166 pub target_key: String,
168 pub accepted: bool,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub error_code: Option<ErrorCode>,
173}
174
175pub fn v1_to_v2_command(cmd: &str, args: &serde_json::Value) -> serde_json::Value {
190 use serde_json::json;
191
192 match cmd {
193 "ping" => json!({"type": "ping"}),
195 "metrics" => json!({"type": "metrics"}),
196 "get" => json!({"type": "get", "key": args["key"]}),
197 "hook_evaluate" => json!({
198 "type": "hook_evaluate",
199 "file_key": args["file_key"],
200 "include_recent": args.get("include_recent").and_then(|v| v.as_bool()).unwrap_or(false),
201 "actor": args["actor"],
202 }),
203 "scan_prefix" => json!({"type": "scan_prefix", "prefix": args["prefix"]}),
204 "scan_keys" => json!({"type": "scan_keys", "prefix": args["prefix"]}),
205 "history" => {
206 json!({"type": "history", "key": args["key"], "limit": args.get("limit").and_then(|v| v.as_u64()).unwrap_or(50)})
207 }
208 "history_since" => json!({
209 "type": "history_since",
210 "key": args["key"],
211 "since_ts": args.get("since_ts").and_then(|v| v.as_u64()).unwrap_or(0),
212 "limit": args.get("limit").and_then(|v| v.as_u64()).unwrap_or(50),
213 }),
214 "session_check_consulted" => json!({"type": "session_check_consulted", "key": args["key"]}),
215 "session_check_consulted_recent" => json!({
216 "type": "session_check_consulted_recent",
217 "key": args["key"],
218 "ttl_secs": args.get("ttl_secs").and_then(|v| v.as_u64()).unwrap_or(900),
219 }),
220 "mem_query" => json!({
221 "type": "mem_query",
222 "query": args["query"],
223 "mode": args.get("mode").and_then(|v| v.as_str()).unwrap_or("text"),
224 "limit": args.get("limit").and_then(|v| v.as_u64()).unwrap_or(20),
225 "since": args.get("since").and_then(|v| v.as_u64()),
226 }),
227 "scan_enforcement_events" => json!({
228 "type": "scan_enforcement_events",
229 "since_seq": args.get("since_seq").and_then(|v| v.as_u64()).unwrap_or(0),
230 "until_seq": args.get("until_seq").and_then(|v| v.as_u64()).unwrap_or(u64::MAX),
231 }),
232 "scan_enforcement_events_with_skips" => json!({
233 "type": "scan_enforcement_events_with_skips",
234 "since_seq": args.get("since_seq").and_then(|v| v.as_u64()).unwrap_or(0),
235 "until_seq": args.get("until_seq").and_then(|v| v.as_u64()).unwrap_or(u64::MAX),
236 }),
237 "scan_enforcement_events_since_ms" => json!({
238 "type": "scan_enforcement_events_since_ms",
239 "since_ms": args.get("since_ms").and_then(|v| v.as_u64()).unwrap_or(0),
240 "until_ms": args.get("until_ms").and_then(|v| v.as_u64()).unwrap_or(u64::MAX),
241 }),
242 "mem_get" => json!({"type": "mem_get", "key": args["key"], "actor": args["actor"]}),
250 "mem_bootstrap" => json!({
251 "type": "mem_bootstrap",
252 "context_files": args.get("context_files").cloned().unwrap_or_else(|| serde_json::json!([])),
253 }),
254 other => {
255 panic!(
256 "v1_to_v2_command called with unsupported command '{other}' — \
257 only pure reads are supported; mutation/side-effecting callers \
258 must use daemon_v2() with typed Command"
259 );
260 }
261 }
262}
263
264