/** * @license * Copyright 2025 Qwen Team * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { useState } from 'react'; export interface PermissionOption { name: string; kind: string; optionId: string; } export interface ToolCall { title?: string; kind?: string; toolCallId?: string; rawInput?: { command?: string; description?: string; [key: string]: unknown; }; content?: Array<{ type: string; [key: string]: unknown; }>; locations?: Array<{ path: string; line?: number | null; }>; status?: string; } export interface PermissionRequestProps { options: PermissionOption[]; toolCall: ToolCall; onResponse: (optionId: string) => void; } export const PermissionRequest: React.FC = ({ options, toolCall, onResponse, }) => { const [selected, setSelected] = useState(null); const [isResponding, setIsResponding] = useState(false); const [hasResponded, setHasResponded] = useState(false); const getToolInfo = () => { if (!toolCall) { return { title: 'Permission Request', description: 'Agent is requesting permission', icon: '🔐', }; } const displayTitle = toolCall.title || toolCall.rawInput?.description || 'Permission Request'; const kindIcons: Record = { edit: '✏️', read: '📖', fetch: '🌐', execute: '⚡', delete: '🗑️', move: '📦', search: '🔍', think: '💭', other: '🔧', }; return { title: displayTitle, icon: kindIcons[toolCall.kind || 'other'] || '🔧', }; }; const { title, icon } = getToolInfo(); const handleConfirm = async () => { if (hasResponded || !selected) { return; } setIsResponding(true); try { await onResponse(selected); setHasResponded(true); } catch (error) { console.error('Error confirming permission:', error); } finally { setIsResponding(false); } }; if (!toolCall) { return null; } return (
{/* Header with icon and title */}
{icon}
{title}
Waiting for your approval
{/* Show command if available */} {(toolCall.rawInput?.command || toolCall.title) && (
COMMAND
IN {toolCall.rawInput?.command || toolCall.title}
{toolCall.rawInput?.description && (
{toolCall.rawInput.description}
)}
)} {/* Show file locations if available */} {toolCall.locations && toolCall.locations.length > 0 && (
Affected Files
{toolCall.locations.map((location, index) => (
📄 {location.path} {location.line !== null && location.line !== undefined && ( ::{location.line} )}
))}
)} {/* Options */} {!hasResponded && (
Choose an action:
{options && options.length > 0 ? ( options.map((option, index) => { const isSelected = selected === option.optionId; const isAllow = option.kind.includes('allow'); const isAlways = option.kind.includes('always'); return ( ); }) ) : (
No options available
)}
)} {/* Success message */} {hasResponded && (
Response sent successfully
)}
); };