1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9pub const EXTERNAL_PERMISSION_MODES: &[&str] = &[
15 "acceptEdits",
16 "bypassPermissions",
17 "default",
18 "dontAsk",
19 "plan",
20];
21
22pub type ExternalPermissionMode = String;
24
25pub type InternalPermissionMode = String;
27
28pub type PermissionMode = String;
30
31pub const INTERNAL_PERMISSION_MODES: &[&str] = &[
33 "acceptEdits",
34 "bypassPermissions",
35 "default",
36 "dontAsk",
37 "plan",
38 "auto",
39];
40
41pub const PERMISSION_MODES: &[&str] = INTERNAL_PERMISSION_MODES;
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
50#[serde(rename_all = "lowercase")]
51pub enum PermissionBehavior {
52 Allow,
53 Deny,
54 Ask,
55}
56
57impl PermissionBehavior {
58 pub fn as_str(&self) -> &'static str {
59 match self {
60 PermissionBehavior::Allow => "allow",
61 PermissionBehavior::Deny => "deny",
62 PermissionBehavior::Ask => "ask",
63 }
64 }
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
73#[serde(rename_all = "camelCase")]
74pub enum PermissionRuleSource {
75 UserSettings,
76 ProjectSettings,
77 LocalSettings,
78 FlagSettings,
79 PolicySettings,
80 CliArg,
81 Command,
82 Session,
83}
84
85impl PermissionRuleSource {
86 pub fn as_str(&self) -> &'static str {
87 match self {
88 PermissionRuleSource::UserSettings => "userSettings",
89 PermissionRuleSource::ProjectSettings => "projectSettings",
90 PermissionRuleSource::LocalSettings => "localSettings",
91 PermissionRuleSource::FlagSettings => "flagSettings",
92 PermissionRuleSource::PolicySettings => "policySettings",
93 PermissionRuleSource::CliArg => "cliArg",
94 PermissionRuleSource::Command => "command",
95 PermissionRuleSource::Session => "session",
96 }
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct PermissionRuleValue {
103 #[serde(rename = "toolName")]
104 pub tool_name: String,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 #[serde(rename = "ruleContent")]
107 pub rule_content: Option<String>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct PermissionRule {
113 pub source: PermissionRuleSource,
114 #[serde(rename = "ruleBehavior")]
115 pub rule_behavior: PermissionBehavior,
116 #[serde(rename = "ruleValue")]
117 pub rule_value: PermissionRuleValue,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
126#[serde(rename_all = "camelCase")]
127pub enum PermissionUpdateDestination {
128 UserSettings,
129 ProjectSettings,
130 LocalSettings,
131 Session,
132 CliArg,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(tag = "type")]
138pub enum PermissionUpdate {
139 #[serde(rename = "addRules")]
140 AddRules {
141 destination: PermissionUpdateDestination,
142 rules: Vec<PermissionRuleValue>,
143 behavior: PermissionBehavior,
144 },
145 #[serde(rename = "replaceRules")]
146 ReplaceRules {
147 destination: PermissionUpdateDestination,
148 rules: Vec<PermissionRuleValue>,
149 behavior: PermissionBehavior,
150 },
151 #[serde(rename = "removeRules")]
152 RemoveRules {
153 destination: PermissionUpdateDestination,
154 rules: Vec<PermissionRuleValue>,
155 behavior: PermissionBehavior,
156 },
157 #[serde(rename = "setMode")]
158 SetMode {
159 destination: PermissionUpdateDestination,
160 mode: ExternalPermissionMode,
161 },
162 #[serde(rename = "addDirectories")]
163 AddDirectories {
164 destination: PermissionUpdateDestination,
165 directories: Vec<String>,
166 },
167 #[serde(rename = "removeDirectories")]
168 RemoveDirectories {
169 destination: PermissionUpdateDestination,
170 directories: Vec<String>,
171 },
172}
173
174impl PermissionUpdate {
175 pub fn type_name(&self) -> &'static str {
177 match self {
178 PermissionUpdate::AddRules { .. } => "addRules",
179 PermissionUpdate::ReplaceRules { .. } => "replaceRules",
180 PermissionUpdate::RemoveRules { .. } => "removeRules",
181 PermissionUpdate::SetMode { .. } => "setMode",
182 PermissionUpdate::AddDirectories { .. } => "addDirectories",
183 PermissionUpdate::RemoveDirectories { .. } => "removeDirectories",
184 }
185 }
186}
187
188pub type WorkingDirectorySource = PermissionRuleSource;
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct AdditionalWorkingDirectory {
194 pub path: String,
195 pub source: WorkingDirectorySource,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct PermissionCommandMetadata {
205 pub name: String,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub description: Option<String>,
208 #[serde(flatten)]
210 pub extra: HashMap<String, serde_json::Value>,
211}
212
213pub type PermissionMetadata = Option<PermissionCommandMetadata>;
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct PermissionAllowDecision {
219 pub behavior: String, #[serde(skip_serializing_if = "Option::is_none")]
221 #[serde(rename = "updatedInput")]
222 pub updated_input: Option<HashMap<String, serde_json::Value>>,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 #[serde(rename = "userModified")]
225 pub user_modified: Option<bool>,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 #[serde(rename = "decisionReason")]
228 pub decision_reason: Option<PermissionDecisionReason>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 #[serde(rename = "toolUseID")]
231 pub tool_use_id: Option<String>,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 #[serde(rename = "acceptFeedback")]
234 pub accept_feedback: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 #[serde(rename = "contentBlocks")]
237 pub content_blocks: Option<Vec<serde_json::Value>>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct PendingClassifierCheck {
243 pub command: String,
244 pub cwd: String,
245 pub descriptions: Vec<String>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct PermissionAskDecision {
251 pub behavior: String, pub message: String,
253 #[serde(skip_serializing_if = "Option::is_none")]
254 #[serde(rename = "updatedInput")]
255 pub updated_input: Option<HashMap<String, serde_json::Value>>,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 #[serde(rename = "decisionReason")]
258 pub decision_reason: Option<PermissionDecisionReason>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub suggestions: Option<Vec<PermissionUpdate>>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 #[serde(rename = "blockedPath")]
263 pub blocked_path: Option<String>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub metadata: Option<PermissionMetadata>,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 #[serde(rename = "isBashSecurityCheckForMisparsing")]
269 pub is_bash_security_check_for_misparsing: Option<bool>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 #[serde(rename = "pendingClassifierCheck")]
273 pub pending_classifier_check: Option<PendingClassifierCheck>,
274 #[serde(skip_serializing_if = "Option::is_none")]
276 #[serde(rename = "contentBlocks")]
277 pub content_blocks: Option<Vec<serde_json::Value>>,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct PermissionDenyDecision {
283 pub behavior: String, pub message: String,
285 #[serde(rename = "decisionReason")]
286 pub decision_reason: PermissionDecisionReason,
287 #[serde(skip_serializing_if = "Option::is_none")]
288 #[serde(rename = "toolUseID")]
289 pub tool_use_id: Option<String>,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
294#[serde(tag = "behavior")]
295pub enum PermissionDecision {
296 #[serde(rename = "allow")]
297 Allow(PermissionAllowDecision),
298 #[serde(rename = "ask")]
299 Ask(PermissionAskDecision),
300 #[serde(rename = "deny")]
301 Deny(PermissionDenyDecision),
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306#[serde(tag = "behavior")]
307pub enum PermissionResult {
308 #[serde(rename = "allow")]
309 Allow(PermissionAllowDecision),
310 #[serde(rename = "ask")]
311 Ask(PermissionAskDecision),
312 #[serde(rename = "deny")]
313 Deny(PermissionDenyDecision),
314 #[serde(rename = "passthrough")]
315 Passthrough {
316 message: String,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 #[serde(rename = "decisionReason")]
319 decision_reason: Option<serde_json::Value>,
320 #[serde(skip_serializing_if = "Option::is_none")]
321 suggestions: Option<Vec<PermissionUpdate>>,
322 #[serde(skip_serializing_if = "Option::is_none")]
323 #[serde(rename = "blockedPath")]
324 blocked_path: Option<String>,
325 #[serde(skip_serializing_if = "Option::is_none")]
326 #[serde(rename = "pendingClassifierCheck")]
327 pending_classifier_check: Option<PendingClassifierCheck>,
328 },
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
333#[serde(tag = "type")]
334pub enum PermissionDecisionReason {
335 #[serde(rename = "rule")]
336 Rule { rule: PermissionRule },
337 #[serde(rename = "mode")]
338 Mode { mode: PermissionMode },
339 #[serde(rename = "subcommandResults")]
340 SubcommandResults {
341 reasons: HashMap<String, PermissionResult>,
342 },
343 #[serde(rename = "permissionPromptTool")]
344 PermissionPromptTool {
345 #[serde(rename = "permissionPromptToolName")]
346 permission_prompt_tool_name: String,
347 #[serde(rename = "toolResult")]
348 tool_result: serde_json::Value,
349 },
350 #[serde(rename = "hook")]
351 Hook {
352 #[serde(rename = "hookName")]
353 hook_name: String,
354 #[serde(skip_serializing_if = "Option::is_none")]
355 #[serde(rename = "hookSource")]
356 hook_source: Option<String>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 reason: Option<String>,
359 },
360 #[serde(rename = "asyncAgent")]
361 AsyncAgent { reason: String },
362 #[serde(rename = "sandboxOverride")]
363 SandboxOverride { reason: SandboxOverrideReason },
364 #[serde(rename = "classifier")]
365 Classifier { classifier: String, reason: String },
366 #[serde(rename = "workingDir")]
367 WorkingDir { reason: String },
368 #[serde(rename = "safetyCheck")]
369 SafetyCheck {
370 reason: String,
371 #[serde(rename = "classifierApprovable")]
373 classifier_approvable: bool,
374 },
375 #[serde(rename = "other")]
376 Other { reason: String },
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381#[serde(rename_all = "camelCase")]
382pub enum SandboxOverrideReason {
383 ExcludedCommand,
384 DangerouslyDisableSandbox,
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct ClassifierResult {
394 pub matches: bool,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 #[serde(rename = "matchedDescription")]
397 pub matched_description: Option<String>,
398 pub confidence: ClassifierConfidence,
399 pub reason: String,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
404#[serde(rename_all = "lowercase")]
405pub enum ClassifierConfidence {
406 High,
407 Medium,
408 Low,
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
413#[serde(rename_all = "lowercase")]
414pub enum ClassifierBehavior {
415 Deny,
416 Ask,
417 Allow,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct ClassifierUsage {
423 #[serde(rename = "inputTokens")]
424 pub input_tokens: i64,
425 #[serde(rename = "outputTokens")]
426 pub output_tokens: i64,
427 #[serde(rename = "cacheReadInputTokens")]
428 pub cache_read_input_tokens: i64,
429 #[serde(rename = "cacheCreationInputTokens")]
430 pub cache_creation_input_tokens: i64,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct YoloClassifierResult {
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub thinking: Option<String>,
438 #[serde(rename = "shouldBlock")]
439 pub should_block: bool,
440 pub reason: String,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub unavailable: Option<bool>,
443 #[serde(skip_serializing_if = "Option::is_none")]
445 #[serde(rename = "transcriptTooLong")]
446 pub transcript_too_long: Option<bool>,
447 pub model: String,
449 #[serde(skip_serializing_if = "Option::is_none")]
451 pub usage: Option<ClassifierUsage>,
452 #[serde(skip_serializing_if = "Option::is_none")]
454 #[serde(rename = "durationMs")]
455 pub duration_ms: Option<u64>,
456 #[serde(skip_serializing_if = "Option::is_none")]
458 #[serde(rename = "promptLengths")]
459 pub prompt_lengths: Option<ClassifierPromptLengths>,
460 #[serde(skip_serializing_if = "Option::is_none")]
462 #[serde(rename = "errorDumpPath")]
463 pub error_dump_path: Option<String>,
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub stage: Option<ClassifierStage>,
467 #[serde(skip_serializing_if = "Option::is_none")]
469 #[serde(rename = "stage1Usage")]
470 pub stage1_usage: Option<ClassifierUsage>,
471 #[serde(skip_serializing_if = "Option::is_none")]
473 #[serde(rename = "stage1DurationMs")]
474 pub stage1_duration_ms: Option<u64>,
475 #[serde(skip_serializing_if = "Option::is_none")]
477 #[serde(rename = "stage1RequestId")]
478 pub stage1_request_id: Option<String>,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 #[serde(rename = "stage1MsgId")]
482 pub stage1_msg_id: Option<String>,
483 #[serde(skip_serializing_if = "Option::is_none")]
485 #[serde(rename = "stage2Usage")]
486 pub stage2_usage: Option<ClassifierUsage>,
487 #[serde(skip_serializing_if = "Option::is_none")]
489 #[serde(rename = "stage2DurationMs")]
490 pub stage2_duration_ms: Option<u64>,
491 #[serde(skip_serializing_if = "Option::is_none")]
493 #[serde(rename = "stage2RequestId")]
494 pub stage2_request_id: Option<String>,
495 #[serde(skip_serializing_if = "Option::is_none")]
497 #[serde(rename = "stage2MsgId")]
498 pub stage2_msg_id: Option<String>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
503#[serde(rename_all = "lowercase")]
504pub enum ClassifierStage {
505 Fast,
506 Thinking,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct ClassifierPromptLengths {
512 #[serde(rename = "systemPrompt")]
513 pub system_prompt: usize,
514 #[serde(rename = "toolCalls")]
515 pub tool_calls: usize,
516 #[serde(rename = "userPrompts")]
517 pub user_prompts: usize,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
526#[serde(rename_all = "UPPERCASE")]
527pub enum RiskLevel {
528 Low,
529 Medium,
530 High,
531}
532
533#[derive(Debug, Clone, Serialize, Deserialize)]
535pub struct PermissionExplanation {
536 #[serde(rename = "riskLevel")]
537 pub risk_level: RiskLevel,
538 pub explanation: String,
539 pub reasoning: String,
540 pub risk: String,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
549pub struct ToolPermissionRulesBySource {
550 #[serde(skip_serializing_if = "Option::is_none")]
551 #[serde(rename = "userSettings")]
552 pub user_settings: Option<Vec<String>>,
553 #[serde(skip_serializing_if = "Option::is_none")]
554 #[serde(rename = "projectSettings")]
555 pub project_settings: Option<Vec<String>>,
556 #[serde(skip_serializing_if = "Option::is_none")]
557 #[serde(rename = "localSettings")]
558 pub local_settings: Option<Vec<String>>,
559 #[serde(skip_serializing_if = "Option::is_none")]
560 #[serde(rename = "flagSettings")]
561 pub flag_settings: Option<Vec<String>>,
562 #[serde(skip_serializing_if = "Option::is_none")]
563 #[serde(rename = "policySettings")]
564 pub policy_settings: Option<Vec<String>>,
565 #[serde(skip_serializing_if = "Option::is_none")]
566 #[serde(rename = "cliArg")]
567 pub cli_arg: Option<Vec<String>>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 pub command: Option<Vec<String>>,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub session: Option<Vec<String>>,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
576pub struct ToolPermissionContext {
577 pub mode: PermissionMode,
578 #[serde(rename = "additionalWorkingDirectories")]
579 pub additional_working_directories: HashMap<String, AdditionalWorkingDirectory>,
580 #[serde(rename = "alwaysAllowRules")]
581 pub always_allow_rules: ToolPermissionRulesBySource,
582 #[serde(rename = "alwaysDenyRules")]
583 pub always_deny_rules: ToolPermissionRulesBySource,
584 #[serde(rename = "alwaysAskRules")]
585 pub always_ask_rules: ToolPermissionRulesBySource,
586 #[serde(rename = "isBypassPermissionsModeAvailable")]
587 pub is_bypass_permissions_mode_available: bool,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 #[serde(rename = "strippedDangerousRules")]
590 pub stripped_dangerous_rules: Option<ToolPermissionRulesBySource>,
591 #[serde(skip_serializing_if = "Option::is_none")]
592 #[serde(rename = "shouldAvoidPermissionPrompts")]
593 pub should_avoid_permission_prompts: Option<bool>,
594 #[serde(skip_serializing_if = "Option::is_none")]
595 #[serde(rename = "awaitAutomatedChecksBeforeDialog")]
596 pub await_automated_checks_before_dialog: Option<bool>,
597 #[serde(skip_serializing_if = "Option::is_none")]
598 #[serde(rename = "prePlanMode")]
599 pub pre_plan_mode: Option<PermissionMode>,
600}