mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-12 19:55:19 +02:00
feat: show agent and add swap-and-retry on acpx permission card
This commit is contained in:
parent
d5dff0554c
commit
fd5e9a8657
2 changed files with 53 additions and 1 deletions
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue