"use client"; import { ArrowLeftIcon, BotIcon, CheckCircleIcon } from "lucide-react"; import { useRouter } from "next/navigation"; import { useCallback, useMemo, useState } from "react"; import { PromptInput, PromptInputFooter, PromptInputSubmit, PromptInputTextarea, } from "@/components/ai-elements/prompt-input"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ArtifactsProvider } from "@/components/workspace/artifacts"; import { MessageList } from "@/components/workspace/messages"; import { ThreadContext } from "@/components/workspace/messages/context"; import type { Agent } from "@/core/agents"; import { checkAgentName, getAgent } from "@/core/agents/api"; import { useI18n } from "@/core/i18n/hooks"; import { useThreadStream } from "@/core/threads/hooks"; import { uuid } from "@/core/utils/uuid"; import { cn } from "@/lib/utils"; type Step = "name" | "chat"; const NAME_RE = /^[A-Za-z0-9-]+$/; export default function NewAgentPage() { const { t } = useI18n(); const router = useRouter(); // ── Step 1: name form ────────────────────────────────────────────────────── const [step, setStep] = useState("name"); const [nameInput, setNameInput] = useState(""); const [nameError, setNameError] = useState(""); const [isCheckingName, setIsCheckingName] = useState(false); const [agentName, setAgentName] = useState(""); const [agent, setAgent] = useState(null); // ── Step 2: chat ─────────────────────────────────────────────────────────── // Stable thread ID — all turns belong to the same thread const threadId = useMemo(() => uuid(), []); const [thread, sendMessage] = useThreadStream({ context: { mode: "flash", is_bootstrap: true, }, onToolEnd({ name }) { if (name !== "setup_agent" || !agentName) return; getAgent(agentName) .then((fetched) => setAgent(fetched)) .catch(() => { // agent write may not be flushed yet — ignore silently }); }, }); // ── Handlers ─────────────────────────────────────────────────────────────── const handleConfirmName = useCallback(async () => { const trimmed = nameInput.trim(); if (!trimmed) return; if (!NAME_RE.test(trimmed)) { setNameError(t.agents.nameStepInvalidError); return; } setNameError(""); setIsCheckingName(true); try { const result = await checkAgentName(trimmed); if (!result.available) { setNameError(t.agents.nameStepAlreadyExistsError); return; } } catch { setNameError(t.agents.nameStepCheckError); return; } finally { setIsCheckingName(false); } setAgentName(trimmed); setStep("chat"); await sendMessage(threadId, { text: t.agents.nameStepBootstrapMessage.replace("{name}", trimmed), files: [], }); }, [ nameInput, sendMessage, threadId, t.agents.nameStepBootstrapMessage, t.agents.nameStepInvalidError, t.agents.nameStepAlreadyExistsError, t.agents.nameStepCheckError, ]); const handleNameKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); void handleConfirmName(); } }; const handleChatSubmit = useCallback( async (text: string) => { const trimmed = text.trim(); if (!trimmed || thread.isLoading) return; await sendMessage( threadId, { text: trimmed, files: [] }, { agent_name: agentName }, ); }, [thread.isLoading, sendMessage, threadId, agentName], ); // ── Shared header ────────────────────────────────────────────────────────── const header = (

{t.agents.createPageTitle}

); // ── Step 1: name form ────────────────────────────────────────────────────── if (step === "name") { return (
{header}

{t.agents.nameStepTitle}

{t.agents.nameStepHint}

{ setNameInput(e.target.value); setNameError(""); }} onKeyDown={handleNameKeyDown} className={cn(nameError && "border-destructive")} /> {nameError && (

{nameError}

)}
); } // ── Step 2: chat ─────────────────────────────────────────────────────────── return (
{header}
{/* ── Message area ── */}
{/* ── Bottom action area ── */}
{agent ? ( // ✅ Success card

{t.agents.agentCreated}

) : ( // 📝 Normal input void handleChatSubmit(text)} > )}
); }