import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from '../../../services/analytics/index.js' import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js' import type { ToolPermissionContext } from '../../../Tool.js' import { CLAUDE_FOLDER_PERMISSION_PATTERN, FILE_EDIT_TOOL_NAME, GLOBAL_CLAUDE_FOLDER_PERMISSION_PATTERN, } from '../../../tools/FileEditTool/constants.js' import { env } from '../../../utils/env.js' import { generateSuggestions } from '../../../utils/permissions/filesystem.js' import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js' import { type CompletionType, logUnaryEvent, } from '../../../utils/unaryLogging.js' import type { ToolUseConfirm } from '../PermissionRequest.js' import type { FileOperationType, PermissionOption, } from './permissionOptions.js' function logPermissionEvent( event: 'accept' | 'reject', completionType: CompletionType, languageName: string | Promise, messageId: string, hasFeedback?: boolean, ): void { void logUnaryEvent({ completion_type: completionType, event, metadata: { language_name: languageName, message_id: messageId, platform: env.platform, hasFeedback: hasFeedback ?? false, }, }) } export type PermissionHandlerParams = { messageId: string path: string | null toolUseConfirm: ToolUseConfirm toolPermissionContext: ToolPermissionContext onDone: () => void onReject: () => void completionType: CompletionType languageName: string | Promise operationType: FileOperationType } export type PermissionHandlerOptions = { hasFeedback?: boolean feedback?: string enteredFeedbackMode?: boolean scope?: 'claude-folder' | 'global-claude-folder' } function handleAcceptOnce( params: PermissionHandlerParams, options?: PermissionHandlerOptions, ): void { const { messageId, toolUseConfirm, onDone, completionType, languageName } = params logPermissionEvent('accept', completionType, languageName, messageId) // Log accept submission with feedback context logEvent('tengu_accept_submitted', { toolName: sanitizeToolNameForAnalytics( toolUseConfirm.tool.name, ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, isMcp: toolUseConfirm.tool.isMcp ?? false, has_instructions: !!options?.feedback, instructions_length: options?.feedback?.length ?? 0, entered_feedback_mode: options?.enteredFeedbackMode ?? false, }) onDone() toolUseConfirm.onAllow(toolUseConfirm.input, [], options?.feedback) } function handleAcceptSession( params: PermissionHandlerParams, options?: PermissionHandlerOptions, ): void { const { messageId, path, toolUseConfirm, toolPermissionContext, onDone, completionType, languageName, operationType, } = params logPermissionEvent('accept', completionType, languageName, messageId) // For claude-folder scope, grant session-level access to all .claude/ files if ( options?.scope === 'claude-folder' || options?.scope === 'global-claude-folder' ) { const pattern = options.scope === 'global-claude-folder' ? GLOBAL_CLAUDE_FOLDER_PERMISSION_PATTERN : CLAUDE_FOLDER_PERMISSION_PATTERN const suggestions: PermissionUpdate[] = [ { type: 'addRules', rules: [ { toolName: FILE_EDIT_TOOL_NAME, ruleContent: pattern, }, ], behavior: 'allow', destination: 'session', }, ] onDone() toolUseConfirm.onAllow(toolUseConfirm.input, suggestions) return } // Generate permission updates if path is provided const suggestions = path ? generateSuggestions(path, operationType, toolPermissionContext) : [] onDone() // Pass permission updates directly to onAllow toolUseConfirm.onAllow(toolUseConfirm.input, suggestions) } function handleReject( params: PermissionHandlerParams, options?: PermissionHandlerOptions, ): void { const { messageId, toolUseConfirm, onDone, onReject, completionType, languageName, } = params logPermissionEvent( 'reject', completionType, languageName, messageId, options?.hasFeedback, ) // Log reject submission with feedback context logEvent('tengu_reject_submitted', { toolName: sanitizeToolNameForAnalytics( toolUseConfirm.tool.name, ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, isMcp: toolUseConfirm.tool.isMcp ?? false, has_instructions: !!options?.feedback, instructions_length: options?.feedback?.length ?? 0, entered_feedback_mode: options?.enteredFeedbackMode ?? false, }) onDone() onReject() toolUseConfirm.onReject(options?.feedback) } export const PERMISSION_HANDLERS: Record< PermissionOption['type'], (params: PermissionHandlerParams, options?: PermissionHandlerOptions) => void > = { 'accept-once': handleAcceptOnce, 'accept-session': handleAcceptSession, reject: handleReject, }