import { useState, useEffect, useCallback } from "react"; import type { AgentCapability, ModelInfo } from "../api"; import { createAgent, fetchModels } from "../api"; import { CustomModelDropdown } from "./CustomModelDropdown"; import { ProviderIcon } from "./ProviderIcon"; export interface NewAgentDialogProps { isOpen: boolean; onClose: () => void; onCreated: () => void; projectId?: string; } const AGENT_ROLES: { value: AgentCapability; label: string; icon: string }[] = [ { value: "triage", label: "Triage", icon: "🔍" }, { value: "executor", label: "Executor", icon: "⚡" }, { value: "reviewer", label: "Reviewer", icon: "👁" }, { value: "merger", label: "Merger", icon: "🔀" }, { value: "scheduler", label: "Scheduler", icon: "⏰" }, { value: "engineer", label: "Engineer", icon: "🛠" }, { value: "custom", label: "Custom", icon: "🔧" }, ]; type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high"; interface RuntimeConfig { model: string; thinkingLevel: ThinkingLevel; maxTurns: number; } export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAgentDialogProps) { const [step, setStep] = useState(0); const [name, setName] = useState(""); const [title, setTitle] = useState(""); const [role, setRole] = useState("custom"); const [runtimeConfig, setRuntimeConfig] = useState({ model: "", thinkingLevel: "off", maxTurns: 10, }); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); // Model dropdown state const [availableModels, setAvailableModels] = useState([]); const [modelsLoading, setModelsLoading] = useState(false); const [favoriteProviders, setFavoriteProviders] = useState([]); const [favoriteModels, setFavoriteModels] = useState([]); // Load models on mount (global data, not per-agent) useEffect(() => { setModelsLoading(true); fetchModels() .then((response) => { setAvailableModels(response.models); setFavoriteProviders(response.favoriteProviders); setFavoriteModels(response.favoriteModels); }) .catch(() => { // Gracefully handle — dropdown will show empty list }) .finally(() => setModelsLoading(false)); }, []); // Selected model in "provider/modelId" format, or "" for default const selectedModel = runtimeConfig.model.includes("/") ? runtimeConfig.model : ""; const handleModelChange = useCallback((value: string) => { // value is "provider/modelId" or "" for default setRuntimeConfig(c => ({ ...c, model: value })); }, []); const handleToggleFavorite = useCallback(async (provider: string) => { const currentFavorites = favoriteProviders; const isFavorite = currentFavorites.includes(provider); const newFavorites = isFavorite ? currentFavorites.filter(p => p !== provider) : [provider, ...currentFavorites]; setFavoriteProviders(newFavorites); }, [favoriteProviders]); const handleToggleModelFavorite = useCallback(async (modelId: string) => { const currentFavorites = favoriteModels; const isFavorite = currentFavorites.includes(modelId); const newFavorites = isFavorite ? currentFavorites.filter(m => m !== modelId) : [modelId, ...currentFavorites]; setFavoriteModels(newFavorites); }, [favoriteModels]); if (!isOpen) return null; const handleClose = () => { setStep(0); setName(""); setTitle(""); setRole("custom"); setRuntimeConfig({ model: "", thinkingLevel: "off", maxTurns: 10 }); setError(null); onClose(); }; const handleCreate = async () => { if (!name.trim()) return; setIsSubmitting(true); setError(null); try { const runtimeCfg: Record = {}; if (runtimeConfig.model.trim()) runtimeCfg.model = runtimeConfig.model.trim(); if (runtimeConfig.thinkingLevel !== "off") runtimeCfg.thinkingLevel = runtimeConfig.thinkingLevel; if (runtimeConfig.maxTurns !== 10) runtimeCfg.maxTurns = runtimeConfig.maxTurns; await createAgent({ name: name.trim(), role, ...(title.trim() ? { title: title.trim() } : {}), ...(Object.keys(runtimeCfg).length > 0 ? { runtimeConfig: runtimeCfg } : {}), }, projectId); handleClose(); onCreated(); } catch (err: unknown) { setError(err instanceof Error ? err.message : "Failed to create agent"); } finally { setIsSubmitting(false); } }; const selectedRole = AGENT_ROLES.find(r => r.value === role); return (
{ if (e.target === e.currentTarget) handleClose(); }}>
{/* Header */}
New Agent
{/* Step indicator */}
{[0, 1, 2].map(i => (
))}
{/* Body */}
{step === 0 && (
setName(e.target.value)} autoFocus style={{ width: "100%", boxSizing: "border-box" }} />
setTitle(e.target.value)} style={{ width: "100%", boxSizing: "border-box" }} />
{AGENT_ROLES.map(r => ( ))}
)} {step === 1 && (
{modelsLoading ? (
Loading models…
) : ( )}
setRuntimeConfig(c => ({ ...c, maxTurns: Math.max(1, parseInt(e.target.value, 10) || 1) }))} style={{ width: "100%", boxSizing: "border-box" }} />
)} {step === 2 && (

Review your agent configuration before creating.

Name {name}
{title && (
Title {title}
)}
Role {selectedRole?.icon} {selectedRole?.label}
Model {selectedModel ? ( <> {" "} {(() => { const slashIdx = selectedModel.indexOf("/"); const provider = selectedModel.slice(0, slashIdx); const modelId = selectedModel.slice(slashIdx + 1); const model = availableModels.find(m => m.provider === provider && m.id === modelId); return model?.name || selectedModel; })()} ) : ( default )}
Thinking {runtimeConfig.thinkingLevel}
Max Turns {runtimeConfig.maxTurns}
{error && (

{error}

)}
)}
{/* Footer */}
{step > 0 && ( )} {step < 2 ? ( ) : ( )}
); }