"use client"; import { useRef, useState } from "react"; import { useSpawnAgent } from "@/hooks/use-agents"; import { getErrorMessage } from "@/lib/api/client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogDescription, } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { HelpTip } from "@/components/ui/help-tip"; import { TaskSelector } from "@/components/tasks/task-selector"; import { Play } from "lucide-react"; import { toast } from "sonner"; interface SpawnAgentDialogProps { agentId: string; agentName: string; trigger?: React.ReactNode; } export function SpawnAgentDialog({ agentId, agentName, trigger, }: SpawnAgentDialogProps) { const [open, setOpen] = useState(false); const [taskId, setTaskId] = useState(null); const [initialPrompt, setInitialPrompt] = useState(""); const spawnAgent = useSpawnAgent(); // Synchronous re-entrancy guard: `spawnAgent.isPending` only flips on a // re-render, which lags a fast double-click/double-fire by a tick or two — // the guard below blocks a second call within the same synchronous burst // regardless of render timing. const submittingRef = useRef(false); const handleSpawn = async () => { if (submittingRef.current) return; submittingRef.current = true; try { const result = await spawnAgent.mutateAsync({ agentId, request: { task_id: taskId || undefined, initial_prompt: initialPrompt || undefined, }, }); if (result.already_running) { toast.info(`Agent ${agentName} already running — spawn skipped`); } else { toast.success(`Agent ${agentName} spawned successfully`); } setOpen(false); resetForm(); } catch (error) { toast.error(getErrorMessage(error)); } finally { submittingRef.current = false; } }; const resetForm = () => { setTaskId(null); setInitialPrompt(""); }; // The tooltip must wrap the DialogTrigger, never sit inside it: with // HelpTip as DialogTrigger's asChild child, the dialog's click handler is // cloned onto the Tooltip root (not a DOM element) and silently dropped. const defaultTrigger = ( e.preventDefault()}> Spawn ); return ( {trigger ? ( {trigger} ) : ( {defaultTrigger} )} Spawn {agentName} Start this agent with optional task assignment and initial prompt.
setInitialPrompt(e.target.value)} placeholder="Initial instructions for the agent" />
); }