init claude-code
This commit is contained in:
@@ -0,0 +1,441 @@
|
||||
/**
|
||||
* Pure permission type definitions extracted to break import cycles.
|
||||
*
|
||||
* This file contains only type definitions and constants with no runtime dependencies.
|
||||
* Implementation files remain in src/utils/permissions/ but can now import from here
|
||||
* to avoid circular dependencies.
|
||||
*/
|
||||
|
||||
import { feature } from 'bun:bundle'
|
||||
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
|
||||
|
||||
// ============================================================================
|
||||
// Permission Modes
|
||||
// ============================================================================
|
||||
|
||||
export const EXTERNAL_PERMISSION_MODES = [
|
||||
'acceptEdits',
|
||||
'bypassPermissions',
|
||||
'default',
|
||||
'dontAsk',
|
||||
'plan',
|
||||
] as const
|
||||
|
||||
export type ExternalPermissionMode = (typeof EXTERNAL_PERMISSION_MODES)[number]
|
||||
|
||||
// Exhaustive mode union for typechecking. The user-addressable runtime set
|
||||
// is INTERNAL_PERMISSION_MODES below.
|
||||
export type InternalPermissionMode = ExternalPermissionMode | 'auto' | 'bubble'
|
||||
export type PermissionMode = InternalPermissionMode
|
||||
|
||||
// Runtime validation set: modes that are user-addressable (settings.json
|
||||
// defaultMode, --permission-mode CLI flag, conversation recovery).
|
||||
export const INTERNAL_PERMISSION_MODES = [
|
||||
...EXTERNAL_PERMISSION_MODES,
|
||||
...(feature('TRANSCRIPT_CLASSIFIER') ? (['auto'] as const) : ([] as const)),
|
||||
] as const satisfies readonly PermissionMode[]
|
||||
|
||||
export const PERMISSION_MODES = INTERNAL_PERMISSION_MODES
|
||||
|
||||
// ============================================================================
|
||||
// Permission Behaviors
|
||||
// ============================================================================
|
||||
|
||||
export type PermissionBehavior = 'allow' | 'deny' | 'ask'
|
||||
|
||||
// ============================================================================
|
||||
// Permission Rules
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Where a permission rule originated from.
|
||||
* Includes all SettingSource values plus additional rule-specific sources.
|
||||
*/
|
||||
export type PermissionRuleSource =
|
||||
| 'userSettings'
|
||||
| 'projectSettings'
|
||||
| 'localSettings'
|
||||
| 'flagSettings'
|
||||
| 'policySettings'
|
||||
| 'cliArg'
|
||||
| 'command'
|
||||
| 'session'
|
||||
|
||||
/**
|
||||
* The value of a permission rule - specifies which tool and optional content
|
||||
*/
|
||||
export type PermissionRuleValue = {
|
||||
toolName: string
|
||||
ruleContent?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A permission rule with its source and behavior
|
||||
*/
|
||||
export type PermissionRule = {
|
||||
source: PermissionRuleSource
|
||||
ruleBehavior: PermissionBehavior
|
||||
ruleValue: PermissionRuleValue
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Permission Updates
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Where a permission update should be persisted
|
||||
*/
|
||||
export type PermissionUpdateDestination =
|
||||
| 'userSettings'
|
||||
| 'projectSettings'
|
||||
| 'localSettings'
|
||||
| 'session'
|
||||
| 'cliArg'
|
||||
|
||||
/**
|
||||
* Update operations for permission configuration
|
||||
*/
|
||||
export type PermissionUpdate =
|
||||
| {
|
||||
type: 'addRules'
|
||||
destination: PermissionUpdateDestination
|
||||
rules: PermissionRuleValue[]
|
||||
behavior: PermissionBehavior
|
||||
}
|
||||
| {
|
||||
type: 'replaceRules'
|
||||
destination: PermissionUpdateDestination
|
||||
rules: PermissionRuleValue[]
|
||||
behavior: PermissionBehavior
|
||||
}
|
||||
| {
|
||||
type: 'removeRules'
|
||||
destination: PermissionUpdateDestination
|
||||
rules: PermissionRuleValue[]
|
||||
behavior: PermissionBehavior
|
||||
}
|
||||
| {
|
||||
type: 'setMode'
|
||||
destination: PermissionUpdateDestination
|
||||
mode: ExternalPermissionMode
|
||||
}
|
||||
| {
|
||||
type: 'addDirectories'
|
||||
destination: PermissionUpdateDestination
|
||||
directories: string[]
|
||||
}
|
||||
| {
|
||||
type: 'removeDirectories'
|
||||
destination: PermissionUpdateDestination
|
||||
directories: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Source of an additional working directory permission.
|
||||
* Note: This is currently the same as PermissionRuleSource but kept as a
|
||||
* separate type for semantic clarity and potential future divergence.
|
||||
*/
|
||||
export type WorkingDirectorySource = PermissionRuleSource
|
||||
|
||||
/**
|
||||
* An additional directory included in permission scope
|
||||
*/
|
||||
export type AdditionalWorkingDirectory = {
|
||||
path: string
|
||||
source: WorkingDirectorySource
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Permission Decisions & Results
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Minimal command shape for permission metadata.
|
||||
* This is intentionally a subset of the full Command type to avoid import cycles.
|
||||
* Only includes properties needed by permission-related components.
|
||||
*/
|
||||
export type PermissionCommandMetadata = {
|
||||
name: string
|
||||
description?: string
|
||||
// Allow additional properties for forward compatibility
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata attached to permission decisions
|
||||
*/
|
||||
export type PermissionMetadata =
|
||||
| { command: PermissionCommandMetadata }
|
||||
| undefined
|
||||
|
||||
/**
|
||||
* Result when permission is granted
|
||||
*/
|
||||
export type PermissionAllowDecision<
|
||||
Input extends { [key: string]: unknown } = { [key: string]: unknown },
|
||||
> = {
|
||||
behavior: 'allow'
|
||||
updatedInput?: Input
|
||||
userModified?: boolean
|
||||
decisionReason?: PermissionDecisionReason
|
||||
toolUseID?: string
|
||||
acceptFeedback?: string
|
||||
contentBlocks?: ContentBlockParam[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata for a pending classifier check that will run asynchronously.
|
||||
* Used to enable non-blocking allow classifier evaluation.
|
||||
*/
|
||||
export type PendingClassifierCheck = {
|
||||
command: string
|
||||
cwd: string
|
||||
descriptions: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Result when user should be prompted
|
||||
*/
|
||||
export type PermissionAskDecision<
|
||||
Input extends { [key: string]: unknown } = { [key: string]: unknown },
|
||||
> = {
|
||||
behavior: 'ask'
|
||||
message: string
|
||||
updatedInput?: Input
|
||||
decisionReason?: PermissionDecisionReason
|
||||
suggestions?: PermissionUpdate[]
|
||||
blockedPath?: string
|
||||
metadata?: PermissionMetadata
|
||||
/**
|
||||
* If true, this ask decision was triggered by a bashCommandIsSafe_DEPRECATED security check
|
||||
* for patterns that splitCommand_DEPRECATED could misparse (e.g. line continuations, shell-quote
|
||||
* transformations). Used by bashToolHasPermission to block early before splitCommand_DEPRECATED
|
||||
* transforms the command. Not set for simple newline compound commands.
|
||||
*/
|
||||
isBashSecurityCheckForMisparsing?: boolean
|
||||
/**
|
||||
* If set, an allow classifier check should be run asynchronously.
|
||||
* The classifier may auto-approve the permission before the user responds.
|
||||
*/
|
||||
pendingClassifierCheck?: PendingClassifierCheck
|
||||
/**
|
||||
* Optional content blocks (e.g., images) to include alongside the rejection
|
||||
* message in the tool result. Used when users paste images as feedback.
|
||||
*/
|
||||
contentBlocks?: ContentBlockParam[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Result when permission is denied
|
||||
*/
|
||||
export type PermissionDenyDecision = {
|
||||
behavior: 'deny'
|
||||
message: string
|
||||
decisionReason: PermissionDecisionReason
|
||||
toolUseID?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A permission decision - allow, ask, or deny
|
||||
*/
|
||||
export type PermissionDecision<
|
||||
Input extends { [key: string]: unknown } = { [key: string]: unknown },
|
||||
> =
|
||||
| PermissionAllowDecision<Input>
|
||||
| PermissionAskDecision<Input>
|
||||
| PermissionDenyDecision
|
||||
|
||||
/**
|
||||
* Permission result with additional passthrough option
|
||||
*/
|
||||
export type PermissionResult<
|
||||
Input extends { [key: string]: unknown } = { [key: string]: unknown },
|
||||
> =
|
||||
| PermissionDecision<Input>
|
||||
| {
|
||||
behavior: 'passthrough'
|
||||
message: string
|
||||
decisionReason?: PermissionDecision<Input>['decisionReason']
|
||||
suggestions?: PermissionUpdate[]
|
||||
blockedPath?: string
|
||||
/**
|
||||
* If set, an allow classifier check should be run asynchronously.
|
||||
* The classifier may auto-approve the permission before the user responds.
|
||||
*/
|
||||
pendingClassifierCheck?: PendingClassifierCheck
|
||||
}
|
||||
|
||||
/**
|
||||
* Explanation of why a permission decision was made
|
||||
*/
|
||||
export type PermissionDecisionReason =
|
||||
| {
|
||||
type: 'rule'
|
||||
rule: PermissionRule
|
||||
}
|
||||
| {
|
||||
type: 'mode'
|
||||
mode: PermissionMode
|
||||
}
|
||||
| {
|
||||
type: 'subcommandResults'
|
||||
reasons: Map<string, PermissionResult>
|
||||
}
|
||||
| {
|
||||
type: 'permissionPromptTool'
|
||||
permissionPromptToolName: string
|
||||
toolResult: unknown
|
||||
}
|
||||
| {
|
||||
type: 'hook'
|
||||
hookName: string
|
||||
hookSource?: string
|
||||
reason?: string
|
||||
}
|
||||
| {
|
||||
type: 'asyncAgent'
|
||||
reason: string
|
||||
}
|
||||
| {
|
||||
type: 'sandboxOverride'
|
||||
reason: 'excludedCommand' | 'dangerouslyDisableSandbox'
|
||||
}
|
||||
| {
|
||||
type: 'classifier'
|
||||
classifier: string
|
||||
reason: string
|
||||
}
|
||||
| {
|
||||
type: 'workingDir'
|
||||
reason: string
|
||||
}
|
||||
| {
|
||||
type: 'safetyCheck'
|
||||
reason: string
|
||||
// When true, auto mode lets the classifier evaluate this instead of
|
||||
// forcing a prompt. True for sensitive-file paths (.claude/, .git/,
|
||||
// shell configs) — the classifier can see context and decide. False
|
||||
// for Windows path bypass attempts and cross-machine bridge messages.
|
||||
classifierApprovable: boolean
|
||||
}
|
||||
| {
|
||||
type: 'other'
|
||||
reason: string
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Bash Classifier Types
|
||||
// ============================================================================
|
||||
|
||||
export type ClassifierResult = {
|
||||
matches: boolean
|
||||
matchedDescription?: string
|
||||
confidence: 'high' | 'medium' | 'low'
|
||||
reason: string
|
||||
}
|
||||
|
||||
export type ClassifierBehavior = 'deny' | 'ask' | 'allow'
|
||||
|
||||
export type ClassifierUsage = {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cacheReadInputTokens: number
|
||||
cacheCreationInputTokens: number
|
||||
}
|
||||
|
||||
export type YoloClassifierResult = {
|
||||
thinking?: string
|
||||
shouldBlock: boolean
|
||||
reason: string
|
||||
unavailable?: boolean
|
||||
/**
|
||||
* API returned "prompt is too long" — the classifier transcript exceeded
|
||||
* the context window. Deterministic (same transcript → same error), so
|
||||
* callers should fall back to normal prompting rather than retry/fail-closed.
|
||||
*/
|
||||
transcriptTooLong?: boolean
|
||||
/** The model used for this classifier call */
|
||||
model: string
|
||||
/** Token usage from the classifier API call (for overhead telemetry) */
|
||||
usage?: ClassifierUsage
|
||||
/** Duration of the classifier API call in ms */
|
||||
durationMs?: number
|
||||
/** Character lengths of the prompt components sent to the classifier */
|
||||
promptLengths?: {
|
||||
systemPrompt: number
|
||||
toolCalls: number
|
||||
userPrompts: number
|
||||
}
|
||||
/** Path where error prompts were dumped (only set when unavailable due to API error) */
|
||||
errorDumpPath?: string
|
||||
/** Which classifier stage produced the final decision (2-stage XML only) */
|
||||
stage?: 'fast' | 'thinking'
|
||||
/** Token usage from stage 1 (fast) when stage 2 was also run */
|
||||
stage1Usage?: ClassifierUsage
|
||||
/** Duration of stage 1 in ms when stage 2 was also run */
|
||||
stage1DurationMs?: number
|
||||
/**
|
||||
* API request_id (req_xxx) for stage 1. Enables joining to server-side
|
||||
* api_usage logs for cache-miss / routing attribution. Also used for the
|
||||
* legacy 1-stage (tool_use) classifier — the single request goes here.
|
||||
*/
|
||||
stage1RequestId?: string
|
||||
/**
|
||||
* API message id (msg_xxx) for stage 1. Enables joining the
|
||||
* tengu_auto_mode_decision analytics event to the classifier's actual
|
||||
* prompt/completion in post-analysis.
|
||||
*/
|
||||
stage1MsgId?: string
|
||||
/** Token usage from stage 2 (thinking) when stage 2 was run */
|
||||
stage2Usage?: ClassifierUsage
|
||||
/** Duration of stage 2 in ms when stage 2 was run */
|
||||
stage2DurationMs?: number
|
||||
/** API request_id for stage 2 (set whenever stage 2 ran) */
|
||||
stage2RequestId?: string
|
||||
/** API message id (msg_xxx) for stage 2 (set whenever stage 2 ran) */
|
||||
stage2MsgId?: string
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Permission Explainer Types
|
||||
// ============================================================================
|
||||
|
||||
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
|
||||
export type PermissionExplanation = {
|
||||
riskLevel: RiskLevel
|
||||
explanation: string
|
||||
reasoning: string
|
||||
risk: string
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tool Permission Context
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Mapping of permission rules by their source
|
||||
*/
|
||||
export type ToolPermissionRulesBySource = {
|
||||
[T in PermissionRuleSource]?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Context needed for permission checking in tools
|
||||
* Note: Uses a simplified DeepImmutable approximation for this types-only file
|
||||
*/
|
||||
export type ToolPermissionContext = {
|
||||
readonly mode: PermissionMode
|
||||
readonly additionalWorkingDirectories: ReadonlyMap<
|
||||
string,
|
||||
AdditionalWorkingDirectory
|
||||
>
|
||||
readonly alwaysAllowRules: ToolPermissionRulesBySource
|
||||
readonly alwaysDenyRules: ToolPermissionRulesBySource
|
||||
readonly alwaysAskRules: ToolPermissionRulesBySource
|
||||
readonly isBypassPermissionsModeAvailable: boolean
|
||||
readonly strippedDangerousRules?: ToolPermissionRulesBySource
|
||||
readonly shouldAvoidPermissionPrompts?: boolean
|
||||
readonly awaitAutomatedChecksBeforeDialog?: boolean
|
||||
readonly prePlanMode?: PermissionMode
|
||||
}
|
||||
Reference in New Issue
Block a user