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:
parent
1fc04f7c77
commit
15eb2e6249
3 changed files with 63 additions and 6 deletions
|
|
@ -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 &&
|
||||||
|
|
|
||||||
|
|
@ -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.",
|
||||||
|
|
|
||||||
|
|
@ -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": "服务未运行,请先启动以进行对话。",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue