From bb42c62e9d06a2308888d12de75cf03bc83e54f6 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 16 Jun 2026 04:00:13 -0700 Subject: [PATCH] [eric] approvals: add Always-approve button + make Deny one-click (drop the buggy reason step) --- .../src/app/pages/AgentChat/AgentChat.tsx | 4 +- .../app/pages/AgentChat/shell/ApprovalBar.tsx | 150 +++++++----------- frontend/src/shared/state/agentsSlice.ts | 4 +- 3 files changed, 59 insertions(+), 99 deletions(-) diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index b85750ab..172c7f21 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -879,8 +879,8 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose if (!isDraft) dispatch(updateThinkingLevel({ sessionId: id, level })); }, [id, isDraft, dispatch]); - const handleApprove = (requestId: string, updatedInput?: Record, trustPattern?: boolean) => { - dispatch(handleApproval({ requestId, behavior: 'allow', updatedInput, trustPattern })); + const handleApprove = (requestId: string, updatedInput?: Record, trustPattern?: boolean, alwaysAllow?: boolean) => { + dispatch(handleApproval({ requestId, behavior: 'allow', updatedInput, trustPattern, setAlwaysAllow: alwaysAllow })); }; const handleDeny = (requestId: string, message?: string) => { diff --git a/frontend/src/app/pages/AgentChat/shell/ApprovalBar.tsx b/frontend/src/app/pages/AgentChat/shell/ApprovalBar.tsx index 09daf483..2ffabc62 100644 --- a/frontend/src/app/pages/AgentChat/shell/ApprovalBar.tsx +++ b/frontend/src/app/pages/AgentChat/shell/ApprovalBar.tsx @@ -11,6 +11,7 @@ import FormControlLabel from '@mui/material/FormControlLabel'; import WarningAmberIcon from '@mui/icons-material/WarningAmber'; import SendIcon from '@mui/icons-material/Send'; import CheckIcon from '@mui/icons-material/Check'; +import DoneAllIcon from '@mui/icons-material/DoneAll'; import CloseIcon from '@mui/icons-material/Close'; import TerminalIcon from '@mui/icons-material/Terminal'; import DescriptionIcon from '@mui/icons-material/Description'; @@ -163,7 +164,7 @@ function getMcpInputSummary(actionName: string, toolInput: Record): interface Props { request: ApprovalRequest; - onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean) => void; + onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean, alwaysAllow?: boolean) => void; onDeny: (requestId: string, message?: string) => void; } @@ -304,7 +305,7 @@ type Answers = Record; export interface QuestionFormProps { request: ApprovalRequest; - onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean) => void; + onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean, alwaysAllow?: boolean) => void; onDeny: (requestId: string, message?: string) => void; compact?: boolean; } @@ -560,8 +561,6 @@ export const QuestionForm: React.FC = ({ request, onApprove, const GenericApprovalBar: React.FC = ({ request, onApprove, onDeny }) => { const c = useClaudeTokens(); - const [denyMessage, setDenyMessage] = useState(''); - const [showDenyInput, setShowDenyInput] = useState(false); const [detailsExpanded, setDetailsExpanded] = useState(false); const [trustPattern, setTrustPattern] = useState(false); @@ -661,26 +660,7 @@ const GenericApprovalBar: React.FC = ({ request, onApprove, onDeny }) => /> )} - {showDenyInput && ( - setDenyMessage(e.target.value)} - fullWidth - size="small" - sx={{ - mb: 1.5, - '& .MuiOutlinedInput-root': { - color: c.text.primary, - fontSize: '0.8rem', - '& fieldset': { borderColor: c.border.strong }, - '&.Mui-focused fieldset': { borderColor: c.status.error }, - }, - }} - /> - )} - - + - {showDenyInput ? ( - - ) : ( + {!isSensitive && ( )} + ); @@ -818,28 +798,7 @@ const GenericApprovalBar: React.FC = ({ request, onApprove, onDeny }) => - {showDenyInput && ( - - setDenyMessage(e.target.value)} - fullWidth - size="small" - autoFocus - sx={{ - '& .MuiOutlinedInput-root': { - color: c.text.primary, - fontSize: '0.8rem', - '& fieldset': { borderColor: c.border.strong }, - '&.Mui-focused fieldset': { borderColor: c.status.error }, - }, - }} - /> - - )} - - + - {showDenyInput ? ( - - ) : ( - - )} + + ); @@ -911,7 +869,7 @@ interface ToolGroup { interface BatchApprovalBarProps { requests: ApprovalRequest[]; - onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean) => void; + onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean, alwaysAllow?: boolean) => void; onDeny: (requestId: string, message?: string) => void; } @@ -1050,7 +1008,7 @@ interface GroupRowProps { group: ToolGroup; expanded: boolean; onToggle: () => void; - onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean) => void; + onApprove: (requestId: string, updatedInput?: Record, trustPattern?: boolean, alwaysAllow?: boolean) => void; onDeny: (requestId: string, message?: string) => void; onApproveGroup: () => void; onDenyGroup: () => void; diff --git a/frontend/src/shared/state/agentsSlice.ts b/frontend/src/shared/state/agentsSlice.ts index d64f52df..7b03fce8 100644 --- a/frontend/src/shared/state/agentsSlice.ts +++ b/frontend/src/shared/state/agentsSlice.ts @@ -406,17 +406,19 @@ export const handleApproval = createAsyncThunk( message, updatedInput, trustPattern, + setAlwaysAllow, }: { requestId: string; behavior: 'allow' | 'deny'; message?: string; updatedInput?: Record; trustPattern?: boolean; + setAlwaysAllow?: boolean; }) => { const res = await fetch(`${AGENTS_API}/approval`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ request_id: requestId, behavior, message, updated_input: updatedInput, trust_pattern: !!trustPattern }), + body: JSON.stringify({ request_id: requestId, behavior, message, updated_input: updatedInput, trust_pattern: !!trustPattern, set_always_allow: !!setAlwaysAllow }), }); if (!res.ok) { throw new Error(`Approval request failed (${res.status})`);