import { useCallback, useEffect, useMemo, useState } from "react"; import type { Agent, AgentOnboardingSummary, ConversationHistoryEntry, ExistingAgentOnboardingConfig, OnboardingMode } from "../api"; import { cancelAgentOnboarding, connectAgentOnboardingStream, respondToAgentOnboarding, startAgentOnboardingStreaming, } from "../api"; import { AGENT_PRESETS } from "./agent-presets"; import { ConversationHistory } from "./ConversationHistory"; import "./ExperimentalAgentOnboardingModal.css"; type ViewState = "initial" | "loading" | "question" | "summary" | "error"; interface ExperimentalAgentOnboardingModalProps { isOpen: boolean; onClose: () => void; onUseDraft: (summary: AgentOnboardingSummary) => void; projectId?: string; existingAgents: Agent[]; mode?: OnboardingMode; existingAgentConfig?: ExistingAgentOnboardingConfig; } export function ExperimentalAgentOnboardingModal({ isOpen, onClose, onUseDraft, projectId, existingAgents, mode = "create", existingAgentConfig, }: ExperimentalAgentOnboardingModalProps) { 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 isEditMode = mode === "edit"; const resetState = useCallback(() => { setViewState("initial"); setIntent(""); setSessionId(null); setCurrentQuestion(""); setCurrentQuestionId("answer"); setAnswer(""); setSummary(null); setError(null); setHistory([]); }, []); const templateOptions = useMemo( () => AGENT_PRESETS.map((preset) => ({ id: preset.id, label: preset.name, description: preset.description })), [], ); 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"); }, }); return () => stream.close(); }, [sessionId, projectId]); const handleClose = async () => { try { if (sessionId) { await cancelAgentOnboarding(sessionId, projectId); } } catch { // Best-effort server-side cleanup; always allow modal dismissal. } finally { resetState(); onClose(); } }; useEffect(() => { if (!isOpen) { resetState(); } }, [isOpen, resetState]); if (!isOpen) return null; const renderSummaryValue = (value: string | number | null | undefined) => { if (value === undefined || value === null || value === "") { return Not set; } return {value}; }; const start = async () => { setViewState("loading"); setError(null); try { const result = await startAgentOnboardingStreaming( intent, { mode, existingAgentConfig, 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 handleConfirmDraft = async () => { if (!summary) return; onUseDraft(summary); await handleClose(); }; return (

AI Interview

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