import { useEffect, useMemo, useState } from "react"; import type { AgentCapability, ConversationHistoryEntry } from "../api"; import { startAgentOnboardingStreaming, respondToAgentOnboarding, retryAgentOnboardingSession, stopAgentOnboardingGeneration, cancelAgentOnboarding, createAgent, connectAgentOnboardingStream, fetchModels, type Agent, type AgentOnboardingSummary, type ModelInfo, } from "../api"; import { AGENT_PRESETS } from "./agent-presets"; import { ConversationHistory } from "./ConversationHistory"; import { CustomModelDropdown } from "./CustomModelDropdown"; import "./AgentOnboardingModal.css"; type ViewState = "initial" | "loading" | "question" | "summary" | "creating" | "error"; interface AgentOnboardingModalProps { isOpen: boolean; onClose: () => void; onCreated: () => void; addToast: (message: string, type?: "success" | "error") => void; projectId?: string; existingAgents: Agent[]; } export function AgentOnboardingModal({ isOpen, onClose, onCreated, addToast, projectId, existingAgents }: AgentOnboardingModalProps) { const [viewState, setViewState] = useState("initial"); const [intent, setIntent] = useState(""); const [sessionId, setSessionId] = useState(null); const [currentQuestion, setCurrentQuestion] = useState(""); const [currentQuestionId, setCurrentQuestionId] = useState("answer"); const [answer, setAnswer] = useState(""); const [summary, setSummary] = useState(null); const [error, setError] = useState(null); const [history, setHistory] = useState([]); const [runtimeMode, setRuntimeMode] = useState<"model" | "runtime">("model"); const [model, setModel] = useState(""); const [availableModels, setAvailableModels] = useState([]); const templateOptions = useMemo( () => AGENT_PRESETS.map((preset) => ({ id: preset.id, label: preset.name, description: preset.description })), [], ); useEffect(() => { void fetchModels().then((data) => setAvailableModels(data.models)).catch(() => setAvailableModels([])); }, []); useEffect(() => { if (!sessionId) return; const stream = connectAgentOnboardingStream(sessionId, projectId, { onThinking: (data) => { setHistory((current) => { const next = [...current]; const last = next[next.length - 1]; if (last && !last.question) { next[next.length - 1] = { ...last, thinkingOutput: `${last.thinkingOutput ?? ""}${data}` }; return next; } return [...next, { response: {}, thinkingOutput: data }]; }); }, onQuestion: (q) => { setCurrentQuestion(q.question); setCurrentQuestionId(q.id); setViewState("question"); }, onSummary: (nextSummary) => { setSummary(nextSummary); setViewState("summary"); }, onError: (message) => { setError(message); setViewState("error"); }, onComplete: () => { if (summary) { setViewState("summary"); } }, onConnectionStateChange: (state) => { if (state === "reconnecting") { setError("Connection lost. Retrying..."); } }, }); return () => stream.close(); }, [sessionId, projectId]); const handleClose = async () => { if (sessionId) { await cancelAgentOnboarding(sessionId, projectId); } onClose(); }; if (!isOpen) return null; const start = async () => { setViewState("loading"); setError(null); try { const result = await startAgentOnboardingStreaming( intent, { existingAgents: existingAgents.map((agent) => ({ id: agent.id, name: agent.name, role: agent.role })), templates: templateOptions, }, projectId, ); setSessionId(result.sessionId); } catch (err) { setError((err as Error).message); setViewState("error"); } }; const submitAnswer = async () => { if (!sessionId) return; setViewState("loading"); setError(null); try { const responsePayload = { [currentQuestionId]: answer }; setHistory((current) => [ ...current, { question: { id: currentQuestionId, type: "text", question: currentQuestion }, response: responsePayload, }, ]); await respondToAgentOnboarding(sessionId, responsePayload, projectId); setAnswer(""); } catch (err) { setError((err as Error).message); setViewState("error"); } }; const createFromSummary = async () => { if (!summary) return; setViewState("creating"); setError(null); try { await createAgent( { name: summary.name, role: summary.role as AgentCapability, title: summary.title, icon: summary.icon, reportsTo: summary.reportsTo, instructionsText: summary.instructionsText, soul: summary.soul, memory: summary.memory, runtimeConfig: { thinkingLevel: summary.thinkingLevel, maxTurns: summary.maxTurns, ...(runtimeMode === "model" && model ? { model } : {}), ...(runtimeMode === "runtime" ? { runtimeHint: "onboarding" } : {}), }, metadata: summary.skills ? { skills: summary.skills } : undefined, }, projectId, ); addToast(`Agent "${summary.name}" created`, "success"); onCreated(); } catch (err) { setError((err as Error).message); setViewState("error"); } }; return (

Agent Onboarding

{history.length > 0 && } {viewState === "initial" && (