From 7da0ed0e3913b4f9d03fc4f5798ace66e0f6a93c Mon Sep 17 00:00:00 2001 From: Huarch Date: Mon, 8 Jun 2026 19:54:25 +0800 Subject: [PATCH] fix(chat): mark aborted permissions --- .../chat/AgentPermissionRequests.tsx | 41 +++++++++++++++---- src/components/chat/GlobalChatbox.types.ts | 1 + .../chat/hooks/agentChatSessionState.ts | 11 +++-- .../useAgentChatSession.actions.test.tsx | 2 +- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/components/chat/AgentPermissionRequests.tsx b/src/components/chat/AgentPermissionRequests.tsx index cf67a8e..4409935 100644 --- a/src/components/chat/AgentPermissionRequests.tsx +++ b/src/components/chat/AgentPermissionRequests.tsx @@ -94,6 +94,7 @@ const getPermissionStatusLabel = (status: NonNullable[nu if (status === "approved_always") return "已始终允许"; if (status === "approved_once") return "已允许一次"; if (status === "rejected") return "已拒绝"; + if (status === "aborted") return "已中断"; if (status === "error") return "提交失败"; if (status === "submitting") return "提交中"; return "等待确认"; @@ -109,6 +110,7 @@ const getPermissionStatusColor = ( if (status === "approved_once") return approvedOncePermissionColor; if (status === "approved_always") return theme.palette.success.main; if (status === "rejected" || status === "error") return theme.palette.error.main; + if (status === "aborted") return theme.palette.text.secondary; return pendingPermissionColor; }; @@ -119,19 +121,23 @@ const getPermissionStatusTextColor = ( if (status === "approved_once") return "#006c78"; if (status === "approved_always") return theme.palette.success.dark; if (status === "rejected" || status === "error") return theme.palette.error.main; + if (status === "aborted") return theme.palette.text.secondary; return "#8a5a00"; }; const PermissionRequestCard = ({ permission, + isRunning, onReply, }: { permission: NonNullable[number]; + isRunning: boolean; onReply: (requestId: string, reply: PermissionReply) => void; }) => { const theme = useTheme(); - const isPending = permission.status === "pending" || permission.status === "error"; - const isSubmitting = permission.status === "submitting"; + const isPending = + isRunning && (permission.status === "pending" || permission.status === "error"); + const isSubmitting = isRunning && permission.status === "submitting"; const primaryValue = getPermissionPrimaryValue(permission); const metadataText = formatMetadata(permission.metadata); const accentColor = getPermissionStatusColor(permission.status, theme); @@ -363,7 +369,13 @@ export const PermissionRequestGroup = ({ const onceCount = permissions.filter((permission) => permission.status === "approved_once").length; const alwaysCount = permissions.filter((permission) => permission.status === "approved_always").length; const rejectedCount = permissions.filter((permission) => permission.status === "rejected").length; - const pendingCount = permissions.length - onceCount - alwaysCount - rejectedCount; + const abortedCount = permissions.filter((permission) => permission.status === "aborted").length; + const pendingCount = permissions.filter( + (permission) => + permission.status === "pending" || + permission.status === "submitting" || + permission.status === "error", + ).length; const hasPendingPermissions = pendingCount > 0; const [expanded, setExpanded] = React.useState(false); const latestPermissions = permissions.slice(-3); @@ -378,9 +390,24 @@ export const PermissionRequestGroup = ({ { label: "允许一次", value: onceCount, color: getPermissionStatusColor("approved_once", theme), textColor: getPermissionStatusTextColor("approved_once", theme) }, { label: "始终允许", value: alwaysCount, color: getPermissionStatusColor("approved_always", theme), textColor: getPermissionStatusTextColor("approved_always", theme) }, { label: "拒绝", value: rejectedCount, color: getPermissionStatusColor("rejected", theme), textColor: getPermissionStatusTextColor("rejected", theme) }, + { label: "中断", value: abortedCount, color: getPermissionStatusColor("aborted", theme), textColor: getPermissionStatusTextColor("aborted", theme) }, ]; - const chipColor = pendingCount > 0 ? getPermissionStatusColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusColor("rejected", theme) : getPermissionStatusColor("approved_always", theme); - const chipTextColor = pendingCount > 0 ? getPermissionStatusTextColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusTextColor("rejected", theme) : getPermissionStatusTextColor("approved_always", theme); + const chipColor = + pendingCount > 0 + ? getPermissionStatusColor("pending", theme) + : abortedCount > 0 + ? getPermissionStatusColor("aborted", theme) + : rejectedCount > 0 + ? getPermissionStatusColor("rejected", theme) + : getPermissionStatusColor("approved_always", theme); + const chipTextColor = + pendingCount > 0 + ? getPermissionStatusTextColor("pending", theme) + : abortedCount > 0 + ? getPermissionStatusTextColor("aborted", theme) + : rejectedCount > 0 + ? getPermissionStatusTextColor("rejected", theme) + : getPermissionStatusTextColor("approved_always", theme); return ( ))} @@ -605,6 +633,7 @@ export const PermissionRequestGroup = ({ ))} @@ -613,5 +642,3 @@ export const PermissionRequestGroup = ({ ); }; - - diff --git a/src/components/chat/GlobalChatbox.types.ts b/src/components/chat/GlobalChatbox.types.ts index c74c2b5..8877e6b 100644 --- a/src/components/chat/GlobalChatbox.types.ts +++ b/src/components/chat/GlobalChatbox.types.ts @@ -33,6 +33,7 @@ export type AgentPermissionStatus = | "approved_once" | "approved_always" | "rejected" + | "aborted" | "error"; export type AgentPermissionRequest = { diff --git a/src/components/chat/hooks/agentChatSessionState.ts b/src/components/chat/hooks/agentChatSessionState.ts index 56d1699..203bb62 100644 --- a/src/components/chat/hooks/agentChatSessionState.ts +++ b/src/components/chat/hooks/agentChatSessionState.ts @@ -364,7 +364,7 @@ export const normalizeSessionTodos = ( return changed ? nextMessages : messages; }; -export const rejectOpenPermissionsAfterAbort = ( +export const abortOpenPermissionsAfterAbort = ( permissions: AgentPermissionRequest[] | undefined, ) => { if (!permissions?.length) return permissions; @@ -380,7 +380,7 @@ export const rejectOpenPermissionsAfterAbort = ( changed = true; return { ...permission, - status: "rejected" as const, + status: "aborted" as const, repliedAt: Date.now(), error: undefined, }; @@ -415,12 +415,12 @@ export const rejectOpenQuestionsAfterAbort = ( export const finalizeAssistantMessageAfterAbort = (message: Message): Message => { const completedProgress = completeRunningProgress(message.progress); const cancelledTodos = cancelRunningTodos(message.todos); - const rejectedPermissions = rejectOpenPermissionsAfterAbort(message.permissions); + const abortedPermissions = abortOpenPermissionsAfterAbort(message.permissions); const rejectedQuestions = rejectOpenQuestionsAfterAbort(message.questions); const hasVisibleOutput = message.content.trim().length > 0 || Boolean(message.artifacts?.length) || - Boolean(rejectedPermissions?.length) || + Boolean(abortedPermissions?.length) || Boolean(rejectedQuestions?.length) || Boolean(completedProgress?.length) || Boolean(cancelledTodos); @@ -434,7 +434,7 @@ export const finalizeAssistantMessageAfterAbort = (message: Message): Message => content: message.content || "⚠️ **请求已中断**", isError: true, progress: completedProgress, - permissions: rejectedPermissions, + permissions: abortedPermissions, questions: rejectedQuestions, todos: cancelledTodos, }; @@ -454,4 +454,3 @@ export const createAssistantMessage = (): Message => ({ role: "assistant", content: "", }); - diff --git a/src/components/chat/hooks/useAgentChatSession.actions.test.tsx b/src/components/chat/hooks/useAgentChatSession.actions.test.tsx index e900682..8149ea2 100644 --- a/src/components/chat/hooks/useAgentChatSession.actions.test.tsx +++ b/src/components/chat/hooks/useAgentChatSession.actions.test.tsx @@ -238,7 +238,7 @@ describe("useAgentChatSession actions", () => { permissions: [ expect.objectContaining({ requestId: "perm-abort", - status: "rejected", + status: "aborted", repliedAt: expect.any(Number), error: undefined, }),