fix(chat): mark aborted permissions
Build Push and Deploy / docker-image (push) Successful in 1m1s
Build Push and Deploy / deploy-fallback-log (push) Has been skipped

This commit is contained in:
2026-06-08 19:54:25 +08:00
parent 166b45e529
commit 7da0ed0e39
4 changed files with 41 additions and 14 deletions
@@ -94,6 +94,7 @@ const getPermissionStatusLabel = (status: NonNullable<Message["permissions"]>[nu
if (status === "approved_always") return "已始终允许"; if (status === "approved_always") return "已始终允许";
if (status === "approved_once") return "已允许一次"; if (status === "approved_once") return "已允许一次";
if (status === "rejected") return "已拒绝"; if (status === "rejected") return "已拒绝";
if (status === "aborted") return "已中断";
if (status === "error") return "提交失败"; if (status === "error") return "提交失败";
if (status === "submitting") return "提交中"; if (status === "submitting") return "提交中";
return "等待确认"; return "等待确认";
@@ -109,6 +110,7 @@ const getPermissionStatusColor = (
if (status === "approved_once") return approvedOncePermissionColor; if (status === "approved_once") return approvedOncePermissionColor;
if (status === "approved_always") return theme.palette.success.main; if (status === "approved_always") return theme.palette.success.main;
if (status === "rejected" || status === "error") return theme.palette.error.main; if (status === "rejected" || status === "error") return theme.palette.error.main;
if (status === "aborted") return theme.palette.text.secondary;
return pendingPermissionColor; return pendingPermissionColor;
}; };
@@ -119,19 +121,23 @@ const getPermissionStatusTextColor = (
if (status === "approved_once") return "#006c78"; if (status === "approved_once") return "#006c78";
if (status === "approved_always") return theme.palette.success.dark; if (status === "approved_always") return theme.palette.success.dark;
if (status === "rejected" || status === "error") return theme.palette.error.main; if (status === "rejected" || status === "error") return theme.palette.error.main;
if (status === "aborted") return theme.palette.text.secondary;
return "#8a5a00"; return "#8a5a00";
}; };
const PermissionRequestCard = ({ const PermissionRequestCard = ({
permission, permission,
isRunning,
onReply, onReply,
}: { }: {
permission: NonNullable<Message["permissions"]>[number]; permission: NonNullable<Message["permissions"]>[number];
isRunning: boolean;
onReply: (requestId: string, reply: PermissionReply) => void; onReply: (requestId: string, reply: PermissionReply) => void;
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const isPending = permission.status === "pending" || permission.status === "error"; const isPending =
const isSubmitting = permission.status === "submitting"; isRunning && (permission.status === "pending" || permission.status === "error");
const isSubmitting = isRunning && permission.status === "submitting";
const primaryValue = getPermissionPrimaryValue(permission); const primaryValue = getPermissionPrimaryValue(permission);
const metadataText = formatMetadata(permission.metadata); const metadataText = formatMetadata(permission.metadata);
const accentColor = getPermissionStatusColor(permission.status, theme); const accentColor = getPermissionStatusColor(permission.status, theme);
@@ -363,7 +369,13 @@ export const PermissionRequestGroup = ({
const onceCount = permissions.filter((permission) => permission.status === "approved_once").length; const onceCount = permissions.filter((permission) => permission.status === "approved_once").length;
const alwaysCount = permissions.filter((permission) => permission.status === "approved_always").length; const alwaysCount = permissions.filter((permission) => permission.status === "approved_always").length;
const rejectedCount = permissions.filter((permission) => permission.status === "rejected").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 hasPendingPermissions = pendingCount > 0;
const [expanded, setExpanded] = React.useState(false); const [expanded, setExpanded] = React.useState(false);
const latestPermissions = permissions.slice(-3); 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: 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: alwaysCount, color: getPermissionStatusColor("approved_always", theme), textColor: getPermissionStatusTextColor("approved_always", theme) },
{ label: "拒绝", value: rejectedCount, color: getPermissionStatusColor("rejected", theme), textColor: getPermissionStatusTextColor("rejected", 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 chipColor =
const chipTextColor = pendingCount > 0 ? getPermissionStatusTextColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusTextColor("rejected", theme) : getPermissionStatusTextColor("approved_always", theme); 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 ( return (
<Box <Box
@@ -591,6 +618,7 @@ export const PermissionRequestGroup = ({
<PermissionRequestCard <PermissionRequestCard
key={permission.requestId} key={permission.requestId}
permission={permission} permission={permission}
isRunning={isRunning}
onReply={onReply} onReply={onReply}
/> />
))} ))}
@@ -605,6 +633,7 @@ export const PermissionRequestGroup = ({
<PermissionRequestCard <PermissionRequestCard
key={permission.requestId} key={permission.requestId}
permission={permission} permission={permission}
isRunning={isRunning}
onReply={onReply} onReply={onReply}
/> />
))} ))}
@@ -613,5 +642,3 @@ export const PermissionRequestGroup = ({
</Box> </Box>
); );
}; };
@@ -33,6 +33,7 @@ export type AgentPermissionStatus =
| "approved_once" | "approved_once"
| "approved_always" | "approved_always"
| "rejected" | "rejected"
| "aborted"
| "error"; | "error";
export type AgentPermissionRequest = { export type AgentPermissionRequest = {
@@ -364,7 +364,7 @@ export const normalizeSessionTodos = (
return changed ? nextMessages : messages; return changed ? nextMessages : messages;
}; };
export const rejectOpenPermissionsAfterAbort = ( export const abortOpenPermissionsAfterAbort = (
permissions: AgentPermissionRequest[] | undefined, permissions: AgentPermissionRequest[] | undefined,
) => { ) => {
if (!permissions?.length) return permissions; if (!permissions?.length) return permissions;
@@ -380,7 +380,7 @@ export const rejectOpenPermissionsAfterAbort = (
changed = true; changed = true;
return { return {
...permission, ...permission,
status: "rejected" as const, status: "aborted" as const,
repliedAt: Date.now(), repliedAt: Date.now(),
error: undefined, error: undefined,
}; };
@@ -415,12 +415,12 @@ export const rejectOpenQuestionsAfterAbort = (
export const finalizeAssistantMessageAfterAbort = (message: Message): Message => { export const finalizeAssistantMessageAfterAbort = (message: Message): Message => {
const completedProgress = completeRunningProgress(message.progress); const completedProgress = completeRunningProgress(message.progress);
const cancelledTodos = cancelRunningTodos(message.todos); const cancelledTodos = cancelRunningTodos(message.todos);
const rejectedPermissions = rejectOpenPermissionsAfterAbort(message.permissions); const abortedPermissions = abortOpenPermissionsAfterAbort(message.permissions);
const rejectedQuestions = rejectOpenQuestionsAfterAbort(message.questions); const rejectedQuestions = rejectOpenQuestionsAfterAbort(message.questions);
const hasVisibleOutput = const hasVisibleOutput =
message.content.trim().length > 0 || message.content.trim().length > 0 ||
Boolean(message.artifacts?.length) || Boolean(message.artifacts?.length) ||
Boolean(rejectedPermissions?.length) || Boolean(abortedPermissions?.length) ||
Boolean(rejectedQuestions?.length) || Boolean(rejectedQuestions?.length) ||
Boolean(completedProgress?.length) || Boolean(completedProgress?.length) ||
Boolean(cancelledTodos); Boolean(cancelledTodos);
@@ -434,7 +434,7 @@ export const finalizeAssistantMessageAfterAbort = (message: Message): Message =>
content: message.content || "⚠️ **请求已中断**", content: message.content || "⚠️ **请求已中断**",
isError: true, isError: true,
progress: completedProgress, progress: completedProgress,
permissions: rejectedPermissions, permissions: abortedPermissions,
questions: rejectedQuestions, questions: rejectedQuestions,
todos: cancelledTodos, todos: cancelledTodos,
}; };
@@ -454,4 +454,3 @@ export const createAssistantMessage = (): Message => ({
role: "assistant", role: "assistant",
content: "", content: "",
}); });
@@ -238,7 +238,7 @@ describe("useAgentChatSession actions", () => {
permissions: [ permissions: [
expect.objectContaining({ expect.objectContaining({
requestId: "perm-abort", requestId: "perm-abort",
status: "rejected", status: "aborted",
repliedAt: expect.any(Number), repliedAt: expect.any(Number),
error: undefined, error: undefined,
}), }),