feat: show agent and add swap-and-retry on acpx permission card

This commit is contained in:
Gagancreates 2026-05-19 04:16:55 +05:30
parent d5dff0554c
commit fd5e9a8657
2 changed files with 53 additions and 1 deletions

View file

@ -5738,6 +5738,24 @@ function App() {
onApproveSession={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'approve', 'session')}
onApproveAlways={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'approve', 'always')}
onDeny={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'deny')}
onSwitchAgent={async (newAgent) => {
const runIdForSwitch = tab.runId
await handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'deny')
window.dispatchEvent(new CustomEvent('code-mode-detected', {
detail: { runId: runIdForSwitch, agent: newAgent },
}))
if (runIdForSwitch) {
try {
await window.ipc.invoke('runs:createMessage', {
runId: runIdForSwitch,
message: `Use ${newAgent === 'claude' ? 'Claude Code' : 'Codex'} instead — rerun the same task with the same prompt, just swap the agent binary to \`${newAgent}\`.`,
codeMode: newAgent,
})
} catch (err) {
console.error('Failed to send swap-agent follow-up', err)
}
}
}}
isProcessing={isActive && isProcessing}
response={response}
/>

View file

@ -9,7 +9,7 @@ import {
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
import { AlertTriangleIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, XCircleIcon, XIcon } from "lucide-react";
import { AlertTriangleIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, RefreshCwIcon, Terminal, XCircleIcon, XIcon } from "lucide-react";
import type { ComponentProps } from "react";
import { ToolCallPart } from "@x/shared/dist/message.js";
import { ToolPermissionMetadata } from "@x/shared/dist/runs.js";
@ -21,6 +21,7 @@ export type PermissionRequestProps = ComponentProps<"div"> & {
onApproveSession?: () => void;
onApproveAlways?: () => void;
onDeny?: () => void;
onSwitchAgent?: (newAgent: 'claude' | 'codex') => void;
isProcessing?: boolean;
response?: 'approve' | 'deny' | null;
permission?: z.infer<typeof ToolPermissionMetadata>;
@ -41,6 +42,7 @@ export const PermissionRequest = ({
onApproveSession,
onApproveAlways,
onDeny,
onSwitchAgent,
isProcessing = false,
response = null,
permission,
@ -54,6 +56,17 @@ export const PermissionRequest = ({
: null;
const filePermission = permission?.kind === "file" ? permission : null;
// Detect acpx coding-agent invocations so we can show the agent identity and
// offer a one-click swap-and-retry.
const acpxAgent: 'claude' | 'codex' | null = (() => {
if (!command) return null;
const match = command.match(/\bacpx\b[\s\S]*?\b(claude|codex)\b\s+exec\b/);
return match ? (match[1] as 'claude' | 'codex') : null;
})();
const otherAgent: 'claude' | 'codex' | null = acpxAgent === 'claude' ? 'codex' : acpxAgent === 'codex' ? 'claude' : null;
const agentDisplay = acpxAgent === 'claude' ? 'Claude Code' : acpxAgent === 'codex' ? 'Codex' : null;
const otherDisplay = otherAgent === 'claude' ? 'Claude Code' : otherAgent === 'codex' ? 'Codex' : null;
const isResponded = response !== null;
const isApproved = response === 'approve';
@ -89,6 +102,15 @@ export const PermissionRequest = ({
</h3>
<p className="text-sm text-muted-foreground mt-1">
{isResponded ? "Requested:" : "The agent wants to execute:"} <span className="font-mono font-medium">{toolCall.toolName}</span>
{agentDisplay && (
<Badge
variant="secondary"
className="ml-2 align-middle bg-secondary text-foreground"
>
<Terminal className="size-3 mr-1" />
{agentDisplay}
</Badge>
)}
</p>
</div>
{isResponded && (
@ -201,6 +223,18 @@ export const PermissionRequest = ({
</DropdownMenu>
)}
</div>
{otherAgent && otherDisplay && onSwitchAgent && (
<Button
variant="secondary"
size="sm"
onClick={() => onSwitchAgent(otherAgent)}
disabled={isProcessing}
className="flex-1"
>
<RefreshCwIcon className="size-4" />
Use {otherDisplay} instead
</Button>
)}
<Button
variant="destructive"
size="sm"