source dump of claude code
at main 441 lines 13 kB view raw
1/** 2 * Pure permission type definitions extracted to break import cycles. 3 * 4 * This file contains only type definitions and constants with no runtime dependencies. 5 * Implementation files remain in src/utils/permissions/ but can now import from here 6 * to avoid circular dependencies. 7 */ 8 9import { feature } from 'bun:bundle' 10import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' 11 12// ============================================================================ 13// Permission Modes 14// ============================================================================ 15 16export const EXTERNAL_PERMISSION_MODES = [ 17 'acceptEdits', 18 'bypassPermissions', 19 'default', 20 'dontAsk', 21 'plan', 22] as const 23 24export type ExternalPermissionMode = (typeof EXTERNAL_PERMISSION_MODES)[number] 25 26// Exhaustive mode union for typechecking. The user-addressable runtime set 27// is INTERNAL_PERMISSION_MODES below. 28export type InternalPermissionMode = ExternalPermissionMode | 'auto' | 'bubble' 29export type PermissionMode = InternalPermissionMode 30 31// Runtime validation set: modes that are user-addressable (settings.json 32// defaultMode, --permission-mode CLI flag, conversation recovery). 33export const INTERNAL_PERMISSION_MODES = [ 34 ...EXTERNAL_PERMISSION_MODES, 35 ...(feature('TRANSCRIPT_CLASSIFIER') ? (['auto'] as const) : ([] as const)), 36] as const satisfies readonly PermissionMode[] 37 38export const PERMISSION_MODES = INTERNAL_PERMISSION_MODES 39 40// ============================================================================ 41// Permission Behaviors 42// ============================================================================ 43 44export type PermissionBehavior = 'allow' | 'deny' | 'ask' 45 46// ============================================================================ 47// Permission Rules 48// ============================================================================ 49 50/** 51 * Where a permission rule originated from. 52 * Includes all SettingSource values plus additional rule-specific sources. 53 */ 54export type PermissionRuleSource = 55 | 'userSettings' 56 | 'projectSettings' 57 | 'localSettings' 58 | 'flagSettings' 59 | 'policySettings' 60 | 'cliArg' 61 | 'command' 62 | 'session' 63 64/** 65 * The value of a permission rule - specifies which tool and optional content 66 */ 67export type PermissionRuleValue = { 68 toolName: string 69 ruleContent?: string 70} 71 72/** 73 * A permission rule with its source and behavior 74 */ 75export type PermissionRule = { 76 source: PermissionRuleSource 77 ruleBehavior: PermissionBehavior 78 ruleValue: PermissionRuleValue 79} 80 81// ============================================================================ 82// Permission Updates 83// ============================================================================ 84 85/** 86 * Where a permission update should be persisted 87 */ 88export type PermissionUpdateDestination = 89 | 'userSettings' 90 | 'projectSettings' 91 | 'localSettings' 92 | 'session' 93 | 'cliArg' 94 95/** 96 * Update operations for permission configuration 97 */ 98export type PermissionUpdate = 99 | { 100 type: 'addRules' 101 destination: PermissionUpdateDestination 102 rules: PermissionRuleValue[] 103 behavior: PermissionBehavior 104 } 105 | { 106 type: 'replaceRules' 107 destination: PermissionUpdateDestination 108 rules: PermissionRuleValue[] 109 behavior: PermissionBehavior 110 } 111 | { 112 type: 'removeRules' 113 destination: PermissionUpdateDestination 114 rules: PermissionRuleValue[] 115 behavior: PermissionBehavior 116 } 117 | { 118 type: 'setMode' 119 destination: PermissionUpdateDestination 120 mode: ExternalPermissionMode 121 } 122 | { 123 type: 'addDirectories' 124 destination: PermissionUpdateDestination 125 directories: string[] 126 } 127 | { 128 type: 'removeDirectories' 129 destination: PermissionUpdateDestination 130 directories: string[] 131 } 132 133/** 134 * Source of an additional working directory permission. 135 * Note: This is currently the same as PermissionRuleSource but kept as a 136 * separate type for semantic clarity and potential future divergence. 137 */ 138export type WorkingDirectorySource = PermissionRuleSource 139 140/** 141 * An additional directory included in permission scope 142 */ 143export type AdditionalWorkingDirectory = { 144 path: string 145 source: WorkingDirectorySource 146} 147 148// ============================================================================ 149// Permission Decisions & Results 150// ============================================================================ 151 152/** 153 * Minimal command shape for permission metadata. 154 * This is intentionally a subset of the full Command type to avoid import cycles. 155 * Only includes properties needed by permission-related components. 156 */ 157export type PermissionCommandMetadata = { 158 name: string 159 description?: string 160 // Allow additional properties for forward compatibility 161 [key: string]: unknown 162} 163 164/** 165 * Metadata attached to permission decisions 166 */ 167export type PermissionMetadata = 168 | { command: PermissionCommandMetadata } 169 | undefined 170 171/** 172 * Result when permission is granted 173 */ 174export type PermissionAllowDecision< 175 Input extends { [key: string]: unknown } = { [key: string]: unknown }, 176> = { 177 behavior: 'allow' 178 updatedInput?: Input 179 userModified?: boolean 180 decisionReason?: PermissionDecisionReason 181 toolUseID?: string 182 acceptFeedback?: string 183 contentBlocks?: ContentBlockParam[] 184} 185 186/** 187 * Metadata for a pending classifier check that will run asynchronously. 188 * Used to enable non-blocking allow classifier evaluation. 189 */ 190export type PendingClassifierCheck = { 191 command: string 192 cwd: string 193 descriptions: string[] 194} 195 196/** 197 * Result when user should be prompted 198 */ 199export type PermissionAskDecision< 200 Input extends { [key: string]: unknown } = { [key: string]: unknown }, 201> = { 202 behavior: 'ask' 203 message: string 204 updatedInput?: Input 205 decisionReason?: PermissionDecisionReason 206 suggestions?: PermissionUpdate[] 207 blockedPath?: string 208 metadata?: PermissionMetadata 209 /** 210 * If true, this ask decision was triggered by a bashCommandIsSafe_DEPRECATED security check 211 * for patterns that splitCommand_DEPRECATED could misparse (e.g. line continuations, shell-quote 212 * transformations). Used by bashToolHasPermission to block early before splitCommand_DEPRECATED 213 * transforms the command. Not set for simple newline compound commands. 214 */ 215 isBashSecurityCheckForMisparsing?: boolean 216 /** 217 * If set, an allow classifier check should be run asynchronously. 218 * The classifier may auto-approve the permission before the user responds. 219 */ 220 pendingClassifierCheck?: PendingClassifierCheck 221 /** 222 * Optional content blocks (e.g., images) to include alongside the rejection 223 * message in the tool result. Used when users paste images as feedback. 224 */ 225 contentBlocks?: ContentBlockParam[] 226} 227 228/** 229 * Result when permission is denied 230 */ 231export type PermissionDenyDecision = { 232 behavior: 'deny' 233 message: string 234 decisionReason: PermissionDecisionReason 235 toolUseID?: string 236} 237 238/** 239 * A permission decision - allow, ask, or deny 240 */ 241export type PermissionDecision< 242 Input extends { [key: string]: unknown } = { [key: string]: unknown }, 243> = 244 | PermissionAllowDecision<Input> 245 | PermissionAskDecision<Input> 246 | PermissionDenyDecision 247 248/** 249 * Permission result with additional passthrough option 250 */ 251export type PermissionResult< 252 Input extends { [key: string]: unknown } = { [key: string]: unknown }, 253> = 254 | PermissionDecision<Input> 255 | { 256 behavior: 'passthrough' 257 message: string 258 decisionReason?: PermissionDecision<Input>['decisionReason'] 259 suggestions?: PermissionUpdate[] 260 blockedPath?: string 261 /** 262 * If set, an allow classifier check should be run asynchronously. 263 * The classifier may auto-approve the permission before the user responds. 264 */ 265 pendingClassifierCheck?: PendingClassifierCheck 266 } 267 268/** 269 * Explanation of why a permission decision was made 270 */ 271export type PermissionDecisionReason = 272 | { 273 type: 'rule' 274 rule: PermissionRule 275 } 276 | { 277 type: 'mode' 278 mode: PermissionMode 279 } 280 | { 281 type: 'subcommandResults' 282 reasons: Map<string, PermissionResult> 283 } 284 | { 285 type: 'permissionPromptTool' 286 permissionPromptToolName: string 287 toolResult: unknown 288 } 289 | { 290 type: 'hook' 291 hookName: string 292 hookSource?: string 293 reason?: string 294 } 295 | { 296 type: 'asyncAgent' 297 reason: string 298 } 299 | { 300 type: 'sandboxOverride' 301 reason: 'excludedCommand' | 'dangerouslyDisableSandbox' 302 } 303 | { 304 type: 'classifier' 305 classifier: string 306 reason: string 307 } 308 | { 309 type: 'workingDir' 310 reason: string 311 } 312 | { 313 type: 'safetyCheck' 314 reason: string 315 // When true, auto mode lets the classifier evaluate this instead of 316 // forcing a prompt. True for sensitive-file paths (.claude/, .git/, 317 // shell configs) — the classifier can see context and decide. False 318 // for Windows path bypass attempts and cross-machine bridge messages. 319 classifierApprovable: boolean 320 } 321 | { 322 type: 'other' 323 reason: string 324 } 325 326// ============================================================================ 327// Bash Classifier Types 328// ============================================================================ 329 330export type ClassifierResult = { 331 matches: boolean 332 matchedDescription?: string 333 confidence: 'high' | 'medium' | 'low' 334 reason: string 335} 336 337export type ClassifierBehavior = 'deny' | 'ask' | 'allow' 338 339export type ClassifierUsage = { 340 inputTokens: number 341 outputTokens: number 342 cacheReadInputTokens: number 343 cacheCreationInputTokens: number 344} 345 346export type YoloClassifierResult = { 347 thinking?: string 348 shouldBlock: boolean 349 reason: string 350 unavailable?: boolean 351 /** 352 * API returned "prompt is too long" — the classifier transcript exceeded 353 * the context window. Deterministic (same transcript → same error), so 354 * callers should fall back to normal prompting rather than retry/fail-closed. 355 */ 356 transcriptTooLong?: boolean 357 /** The model used for this classifier call */ 358 model: string 359 /** Token usage from the classifier API call (for overhead telemetry) */ 360 usage?: ClassifierUsage 361 /** Duration of the classifier API call in ms */ 362 durationMs?: number 363 /** Character lengths of the prompt components sent to the classifier */ 364 promptLengths?: { 365 systemPrompt: number 366 toolCalls: number 367 userPrompts: number 368 } 369 /** Path where error prompts were dumped (only set when unavailable due to API error) */ 370 errorDumpPath?: string 371 /** Which classifier stage produced the final decision (2-stage XML only) */ 372 stage?: 'fast' | 'thinking' 373 /** Token usage from stage 1 (fast) when stage 2 was also run */ 374 stage1Usage?: ClassifierUsage 375 /** Duration of stage 1 in ms when stage 2 was also run */ 376 stage1DurationMs?: number 377 /** 378 * API request_id (req_xxx) for stage 1. Enables joining to server-side 379 * api_usage logs for cache-miss / routing attribution. Also used for the 380 * legacy 1-stage (tool_use) classifier — the single request goes here. 381 */ 382 stage1RequestId?: string 383 /** 384 * API message id (msg_xxx) for stage 1. Enables joining the 385 * tengu_auto_mode_decision analytics event to the classifier's actual 386 * prompt/completion in post-analysis. 387 */ 388 stage1MsgId?: string 389 /** Token usage from stage 2 (thinking) when stage 2 was run */ 390 stage2Usage?: ClassifierUsage 391 /** Duration of stage 2 in ms when stage 2 was run */ 392 stage2DurationMs?: number 393 /** API request_id for stage 2 (set whenever stage 2 ran) */ 394 stage2RequestId?: string 395 /** API message id (msg_xxx) for stage 2 (set whenever stage 2 ran) */ 396 stage2MsgId?: string 397} 398 399// ============================================================================ 400// Permission Explainer Types 401// ============================================================================ 402 403export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' 404 405export type PermissionExplanation = { 406 riskLevel: RiskLevel 407 explanation: string 408 reasoning: string 409 risk: string 410} 411 412// ============================================================================ 413// Tool Permission Context 414// ============================================================================ 415 416/** 417 * Mapping of permission rules by their source 418 */ 419export type ToolPermissionRulesBySource = { 420 [T in PermissionRuleSource]?: string[] 421} 422 423/** 424 * Context needed for permission checking in tools 425 * Note: Uses a simplified DeepImmutable approximation for this types-only file 426 */ 427export type ToolPermissionContext = { 428 readonly mode: PermissionMode 429 readonly additionalWorkingDirectories: ReadonlyMap< 430 string, 431 AdditionalWorkingDirectory 432 > 433 readonly alwaysAllowRules: ToolPermissionRulesBySource 434 readonly alwaysDenyRules: ToolPermissionRulesBySource 435 readonly alwaysAskRules: ToolPermissionRulesBySource 436 readonly isBypassPermissionsModeAvailable: boolean 437 readonly strippedDangerousRules?: ToolPermissionRulesBySource 438 readonly shouldAvoidPermissionPrompts?: boolean 439 readonly awaitAutomatedChecksBeforeDialog?: boolean 440 readonly prePlanMode?: PermissionMode 441}