fix: integrate PermissionPrompt dialog into chat UI

- Add PermissionPrompt component to chat page
- Detect request_permission tool calls and show dialog
- Extract path and command from tool call arguments
- Fix TypeScript errors (null check, unused variable)
- Fix locale files (trailing comma errors)
This commit is contained in:
Dark aura 2026-05-06 04:21:00 +01:00
parent 1fc04f7c77
commit 15eb2e6249
3 changed files with 63 additions and 6 deletions

View file

@ -25,6 +25,7 @@ import type { ConnectionState } from "@/store/chat"
import type { ChatAttachment } from "@/store/chat" import type { ChatAttachment } from "@/store/chat"
import { showAssistantDetailsAtom } from "@/store/chat" import { showAssistantDetailsAtom } from "@/store/chat"
import type { GatewayState } from "@/store/gateway" import type { GatewayState } from "@/store/gateway"
import { PermissionPrompt } from "@/components/permission/PermissionPrompt"
const MAX_IMAGE_SIZE_BYTES = 7 * 1024 * 1024 const MAX_IMAGE_SIZE_BYTES = 7 * 1024 * 1024
const MAX_IMAGE_SIZE_LABEL = "7 MB" const MAX_IMAGE_SIZE_LABEL = "7 MB"
@ -115,6 +116,11 @@ export function ChatPage() {
const [showAssistantDetails, setShowAssistantDetails] = useAtom( const [showAssistantDetails, setShowAssistantDetails] = useAtom(
showAssistantDetailsAtom, showAssistantDetailsAtom,
) )
const [permissionRequest, setPermissionRequest] = useState<{
toolName: string
path: string
originalCommand: string
} | null>(null)
const { const {
messages, messages,
@ -191,6 +197,45 @@ export function ChatPage() {
} }
} }
// Check for permission requests in messages
useEffect(() => {
if (permissionRequest) return // Already showing a permission prompt
// Check the last few messages for request_permission tool calls
for (let i = messages.length - 1; i >= Math.max(0, messages.length - 5); i--) {
const msg = messages[i]
if (msg.role !== "assistant") continue
if (!msg.toolCalls) continue
const permCall = msg.toolCalls.find(
(tc) => tc.function?.name === "request_permission"
)
if (!permCall || !permCall.function) continue
try {
const args = JSON.parse(permCall.function.arguments ?? "{}")
if (args.path && args.command) {
setPermissionRequest({
toolName: "request_permission",
path: args.path,
originalCommand: args.command,
})
return
}
} catch {
// Ignore parse errors
}
}
}, [messages, permissionRequest])
const handlePermissionGranted = () => {
setPermissionRequest(null)
}
const handlePermissionDenied = () => {
setPermissionRequest(null)
}
const handleAddImages = () => { const handleAddImages = () => {
if (!canInput) return if (!canInput) return
fileInputRef.current?.click() fileInputRef.current?.click()
@ -324,6 +369,18 @@ export function ChatPage() {
/> />
)} )}
{permissionRequest && (
<div className="flex w-full">
<PermissionPrompt
toolName={permissionRequest.toolName}
path={permissionRequest.path}
originalCommand={permissionRequest.originalCommand}
onPermissionGranted={handlePermissionGranted}
onPermissionDenied={handlePermissionDenied}
/>
</div>
)}
{messages.map((msg) => { {messages.map((msg) => {
if ( if (
!showAssistantDetails && !showAssistantDetails &&

View file

@ -49,7 +49,7 @@
"websocketConnecting": "Connecting to chat service... Please wait.", "websocketConnecting": "Connecting to chat service... Please wait.",
"websocketDisconnected": "Unable to chat: WebSocket connection is disconnected. Check network and gateway status, then refresh the page or restart Launcher.", "websocketDisconnected": "Unable to chat: WebSocket connection is disconnected. Check network and gateway status, then refresh the page or restart Launcher.",
"websocketError": "Unable to chat: WebSocket connection failed. Check network and gateway status, then retry.", "websocketError": "Unable to chat: WebSocket connection failed. Check network and gateway status, then retry.",
"noDefaultModel": "Unable to chat: No default model is selected. Set a default model on the Models page." "noDefaultModel": "Unable to chat: No default model is selected. Set a default model on the Models page.",
"permissionRequiredTitle": "Permission Required", "permissionRequiredTitle": "Permission Required",
"permissionRequiredMessage": "PicoClaw wants to use {{tool}} to access {{path}} which is outside your workspace.", "permissionRequiredMessage": "PicoClaw wants to use {{tool}} to access {{path}} which is outside your workspace.",
"commandToExecute": "Command to execute:", "commandToExecute": "Command to execute:",
@ -58,8 +58,8 @@
"permissionSession": "Allow for session", "permissionSession": "Allow for session",
"permissionGrantedOnce": "Permission granted for one-time access to {{path}}", "permissionGrantedOnce": "Permission granted for one-time access to {{path}}",
"permissionGrantedSession": "Permission granted for session access to {{path}}", "permissionGrantedSession": "Permission granted for session access to {{path}}",
"permissionDenied": "Permission denied for {{path}}", "permissionDenied": "Permission denied for {{path}}",
"permissionError": "An error occurred while handling the permission request.", "permissionError": "An error occurred while handling the permission request."
}, },
"newChat": "New Chat", "newChat": "New Chat",
"notConnected": "Gateway is not running. Start it to chat.", "notConnected": "Gateway is not running. Start it to chat.",

View file

@ -49,7 +49,7 @@
"websocketConnecting": "正在连接聊天服务,请稍候。", "websocketConnecting": "正在连接聊天服务,请稍候。",
"websocketDisconnected": "无法对话WebSocket 连接已断开。请检查网络与服务状态,然后刷新页面或重启 Launcher。", "websocketDisconnected": "无法对话WebSocket 连接已断开。请检查网络与服务状态,然后刷新页面或重启 Launcher。",
"websocketError": "无法对话WebSocket 连接失败。请检查网络与服务状态后重试。", "websocketError": "无法对话WebSocket 连接失败。请检查网络与服务状态后重试。",
"noDefaultModel": "无法对话:尚未设置默认模型。请前往模型页面设置默认模型。" "noDefaultModel": "无法对话:尚未设置默认模型。请前往模型页面设置默认模型。",
"permissionRequiredTitle": "需要权限", "permissionRequiredTitle": "需要权限",
"permissionRequiredMessage": "PicoClaw 想要使用 {{tool}} 访问 {{path}},但该路径位于工作区之外。", "permissionRequiredMessage": "PicoClaw 想要使用 {{tool}} 访问 {{path}},但该路径位于工作区之外。",
"commandToExecute": "要执行的命令:", "commandToExecute": "要执行的命令:",
@ -58,8 +58,8 @@
"permissionSession": "允许整个会话", "permissionSession": "允许整个会话",
"permissionGrantedOnce": "已授予对 {{path}} 的一次性访问权限", "permissionGrantedOnce": "已授予对 {{path}} 的一次性访问权限",
"permissionGrantedSession": "已授予对 {{path}} 的会话访问权限", "permissionGrantedSession": "已授予对 {{path}} 的会话访问权限",
"permissionDenied": "已拒绝对 {{path}} 的访问权限", "permissionDenied": "已拒绝对 {{path}} 的访问权限",
"permissionError": "处理权限请求时发生错误。", "permissionError": "处理权限请求时发生错误。"
}, },
"newChat": "新建对话", "newChat": "新建对话",
"notConnected": "服务未运行,请先启动以进行对话。", "notConnected": "服务未运行,请先启动以进行对话。",