fix(chat): hide raw permission metadata
This commit is contained in:
@@ -27,32 +27,6 @@ import VerifiedUserRounded from "@mui/icons-material/VerifiedUserRounded";
|
|||||||
import type { PermissionReply } from "@/lib/chatStream";
|
import type { PermissionReply } from "@/lib/chatStream";
|
||||||
import type { Message } from "./GlobalChatbox.types";
|
import type { Message } from "./GlobalChatbox.types";
|
||||||
|
|
||||||
const formatMetadataValue = (value: unknown) => {
|
|
||||||
if (typeof value === "string") {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return JSON.stringify(value);
|
|
||||||
} catch {
|
|
||||||
return "[unserializable]";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const truncateText = (value: string, maxLength: number) =>
|
|
||||||
value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;
|
|
||||||
|
|
||||||
const formatMetadata = (metadata: Record<string, unknown>) => {
|
|
||||||
const entries = Object.entries(metadata)
|
|
||||||
.filter(([key]) => !["command", "path", "file", "directory"].includes(key))
|
|
||||||
.slice(0, 3);
|
|
||||||
if (!entries.length) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return entries
|
|
||||||
.map(([key, value]) => `${key}: ${truncateText(formatMetadataValue(value), 64)}`)
|
|
||||||
.join(";");
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPermissionTitle = (permission: NonNullable<Message["permissions"]>[number]) => {
|
const getPermissionTitle = (permission: NonNullable<Message["permissions"]>[number]) => {
|
||||||
if (permission.permission === "external_directory") return "访问工作区外目录";
|
if (permission.permission === "external_directory") return "访问工作区外目录";
|
||||||
if (permission.permission === "bash") return "执行终端命令";
|
if (permission.permission === "bash") return "执行终端命令";
|
||||||
@@ -63,15 +37,8 @@ const getPermissionTitle = (permission: NonNullable<Message["permissions"]>[numb
|
|||||||
const getPermissionPrimaryValue = (
|
const getPermissionPrimaryValue = (
|
||||||
permission: NonNullable<Message["permissions"]>[number],
|
permission: NonNullable<Message["permissions"]>[number],
|
||||||
) => {
|
) => {
|
||||||
const command = permission.metadata.command;
|
if (typeof permission.target === "string" && permission.target.trim()) {
|
||||||
if (typeof command === "string" && command.trim()) {
|
return permission.target.trim();
|
||||||
return command.trim();
|
|
||||||
}
|
|
||||||
for (const key of ["path", "file", "directory"]) {
|
|
||||||
const value = permission.metadata[key];
|
|
||||||
if (typeof value === "string" && value.trim()) {
|
|
||||||
return value.trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return permission.patterns[0] ?? permission.permission;
|
return permission.patterns[0] ?? permission.permission;
|
||||||
};
|
};
|
||||||
@@ -139,7 +106,6 @@ const PermissionRequestCard = ({
|
|||||||
isRunning && (permission.status === "pending" || permission.status === "error");
|
isRunning && (permission.status === "pending" || permission.status === "error");
|
||||||
const isSubmitting = isRunning && permission.status === "submitting";
|
const isSubmitting = isRunning && permission.status === "submitting";
|
||||||
const primaryValue = getPermissionPrimaryValue(permission);
|
const primaryValue = getPermissionPrimaryValue(permission);
|
||||||
const metadataText = formatMetadata(permission.metadata);
|
|
||||||
const accentColor = getPermissionStatusColor(permission.status, theme);
|
const accentColor = getPermissionStatusColor(permission.status, theme);
|
||||||
const statusTextColor = getPermissionStatusTextColor(permission.status, theme);
|
const statusTextColor = getPermissionStatusTextColor(permission.status, theme);
|
||||||
const statusLabel = getPermissionStatusLabel(permission.status);
|
const statusLabel = getPermissionStatusLabel(permission.status);
|
||||||
@@ -237,12 +203,6 @@ const PermissionRequestCard = ({
|
|||||||
{primaryValue}
|
{primaryValue}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{metadataText ? (
|
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ wordBreak: "break-word" }}>
|
|
||||||
{metadataText}
|
|
||||||
</Typography>
|
|
||||||
) : null}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{permission.error ? (
|
{permission.error ? (
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export type AgentPermissionRequest = {
|
|||||||
sessionId: string;
|
sessionId: string;
|
||||||
permission: string;
|
permission: string;
|
||||||
patterns: string[];
|
patterns: string[];
|
||||||
metadata: Record<string, unknown>;
|
target?: string;
|
||||||
always: string[];
|
always: string[];
|
||||||
tool?: {
|
tool?: {
|
||||||
messageID: string;
|
messageID: string;
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ export const cloneMessage = (message: Message): Message => ({
|
|||||||
? [...permission.patterns]
|
? [...permission.patterns]
|
||||||
: [],
|
: [],
|
||||||
always: Array.isArray(permission.always) ? [...permission.always] : [],
|
always: Array.isArray(permission.always) ? [...permission.always] : [],
|
||||||
metadata: permission.metadata ?? {},
|
|
||||||
}))
|
}))
|
||||||
: undefined,
|
: undefined,
|
||||||
questions: normalizeQuestionRequests(message.questions),
|
questions: normalizeQuestionRequests(message.questions),
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export const upsertPermission = (
|
|||||||
sessionId: event.sessionId,
|
sessionId: event.sessionId,
|
||||||
permission: event.permission,
|
permission: event.permission,
|
||||||
patterns: event.patterns,
|
patterns: event.patterns,
|
||||||
metadata: event.metadata,
|
target: event.target,
|
||||||
always: event.always,
|
always: event.always,
|
||||||
tool: event.tool,
|
tool: event.tool,
|
||||||
createdAt: event.createdAt,
|
createdAt: event.createdAt,
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ describe("useAgentChatSession actions", () => {
|
|||||||
requestId: "perm-1",
|
requestId: "perm-1",
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["rm *"],
|
patterns: ["rm *"],
|
||||||
metadata: { command: "rm tmp.txt" },
|
target: "rm tmp.txt",
|
||||||
always: ["rm *"],
|
always: ["rm *"],
|
||||||
createdAt: 123,
|
createdAt: 123,
|
||||||
});
|
});
|
||||||
@@ -163,7 +163,7 @@ describe("useAgentChatSession actions", () => {
|
|||||||
requestId: "perm-abort",
|
requestId: "perm-abort",
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["npm test"],
|
patterns: ["npm test"],
|
||||||
metadata: { command: "npm test" },
|
target: "npm test",
|
||||||
always: ["npm test"],
|
always: ["npm test"],
|
||||||
createdAt: 1002,
|
createdAt: 1002,
|
||||||
} satisfies StreamEvent);
|
} satisfies StreamEvent);
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ describe("streamAgentChat", () => {
|
|||||||
apiFetch.mockResolvedValue({
|
apiFetch.mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
body: makeStream([
|
body: makeStream([
|
||||||
'event: permission_request\ndata: {"session_id":"s1","request_id":"perm-1","permission":"bash","patterns":["rm *"],"metadata":{"command":"rm tmp.txt"},"always":["rm *"],"created_at":123}\n\n',
|
'event: permission_request\ndata: {"session_id":"s1","request_id":"perm-1","permission":"bash","patterns":["rm *"],"target":"rm tmp.txt","always":["rm *"],"created_at":123}\n\n',
|
||||||
'event: permission_response\ndata: {"session_id":"s1","request_id":"perm-1","reply":"reject"}\n\n',
|
'event: permission_response\ndata: {"session_id":"s1","request_id":"perm-1","reply":"reject"}\n\n',
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
@@ -205,7 +205,7 @@ describe("streamAgentChat", () => {
|
|||||||
requestId: "perm-1",
|
requestId: "perm-1",
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["rm *"],
|
patterns: ["rm *"],
|
||||||
metadata: { command: "rm tmp.txt" },
|
target: "rm tmp.txt",
|
||||||
always: ["rm *"],
|
always: ["rm *"],
|
||||||
tool: undefined,
|
tool: undefined,
|
||||||
createdAt: 123,
|
createdAt: 123,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export type StreamEvent =
|
|||||||
requestId: string;
|
requestId: string;
|
||||||
permission: string;
|
permission: string;
|
||||||
patterns: string[];
|
patterns: string[];
|
||||||
metadata: Record<string, unknown>;
|
target?: string;
|
||||||
always: string[];
|
always: string[];
|
||||||
tool?: {
|
tool?: {
|
||||||
messageID: string;
|
messageID: string;
|
||||||
@@ -296,7 +296,7 @@ const emitParsedStreamEvent = (
|
|||||||
request_id?: string;
|
request_id?: string;
|
||||||
permission?: string;
|
permission?: string;
|
||||||
patterns?: unknown;
|
patterns?: unknown;
|
||||||
metadata?: unknown;
|
target?: string;
|
||||||
always?: unknown;
|
always?: unknown;
|
||||||
created_at?: number;
|
created_at?: number;
|
||||||
reply?: PermissionReply;
|
reply?: PermissionReply;
|
||||||
@@ -370,7 +370,7 @@ const emitParsedStreamEvent = (
|
|||||||
patterns: Array.isArray(parsed.patterns)
|
patterns: Array.isArray(parsed.patterns)
|
||||||
? parsed.patterns.filter((item): item is string => typeof item === "string")
|
? parsed.patterns.filter((item): item is string => typeof item === "string")
|
||||||
: [],
|
: [],
|
||||||
metadata: isObjectRecord(parsed.metadata) ? parsed.metadata : {},
|
target: typeof parsed.target === "string" ? parsed.target : undefined,
|
||||||
always: Array.isArray(parsed.always)
|
always: Array.isArray(parsed.always)
|
||||||
? parsed.always.filter((item): item is string => typeof item === "string")
|
? parsed.always.filter((item): item is string => typeof item === "string")
|
||||||
: [],
|
: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user