"use client"; export const dynamic = "force-dynamic"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "@/auth/clerk"; import { ApiError } from "@/api/mutator"; import { type listBoardsApiV1BoardsGetResponse, useListBoardsApiV1BoardsGet, } from "@/api/generated/boards/boards"; import { useCreateAgentApiV1AgentsPost } from "@/api/generated/agents/agents"; import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { BoardRead } from "@/api/generated/model"; import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import SearchableSelect, { type SearchableSelectOption, } from "@/components/ui/searchable-select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { DEFAULT_IDENTITY_PROFILE } from "@/lib/agent-templates"; type IdentityProfile = { role: string; communication_style: string; emoji: string; }; const EMOJI_OPTIONS = [ { value: ":gear:", label: "Gear", glyph: "βš™οΈ" }, { value: ":sparkles:", label: "Sparkles", glyph: "✨" }, { value: ":rocket:", label: "Rocket", glyph: "πŸš€" }, { value: ":megaphone:", label: "Megaphone", glyph: "πŸ“£" }, { value: ":chart_with_upwards_trend:", label: "Growth", glyph: "πŸ“ˆ" }, { value: ":bulb:", label: "Idea", glyph: "πŸ’‘" }, { value: ":wrench:", label: "Builder", glyph: "πŸ”§" }, { value: ":shield:", label: "Shield", glyph: "πŸ›‘οΈ" }, { value: ":memo:", label: "Notes", glyph: "πŸ“" }, { value: ":brain:", label: "Brain", glyph: "🧠" }, ]; const getBoardOptions = (boards: BoardRead[]): SearchableSelectOption[] => boards.map((board) => ({ value: board.id, label: board.name, })); const normalizeIdentityProfile = ( profile: IdentityProfile, ): IdentityProfile | null => { const normalized: IdentityProfile = { role: profile.role.trim(), communication_style: profile.communication_style.trim(), emoji: profile.emoji.trim(), }; const hasValue = Object.values(normalized).some((value) => value.length > 0); return hasValue ? normalized : null; }; export default function NewAgentPage() { const router = useRouter(); const { isSignedIn } = useAuth(); const { isAdmin } = useOrganizationMembership(isSignedIn); const [name, setName] = useState(""); const [boardId, setBoardId] = useState(""); const [heartbeatEvery, setHeartbeatEvery] = useState("10m"); const [identityProfile, setIdentityProfile] = useState({ ...DEFAULT_IDENTITY_PROFILE, }); const [error, setError] = useState(null); const boardsQuery = useListBoardsApiV1BoardsGet< listBoardsApiV1BoardsGetResponse, ApiError >(undefined, { query: { enabled: Boolean(isSignedIn && isAdmin), refetchOnMount: "always", }, }); const createAgentMutation = useCreateAgentApiV1AgentsPost({ mutation: { onSuccess: (result) => { if (result.status === 200) { router.push(`/agents/${result.data.id}`); } }, onError: (err) => { setError(err.message || "Something went wrong."); }, }, }); const boards = boardsQuery.data?.status === 200 ? (boardsQuery.data.data.items ?? []) : []; const displayBoardId = boardId || boards[0]?.id || ""; const isLoading = boardsQuery.isLoading || createAgentMutation.isPending; const errorMessage = error ?? boardsQuery.error?.message ?? null; const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); if (!isSignedIn) return; const trimmed = name.trim(); if (!trimmed) { setError("Agent name is required."); return; } const resolvedBoardId = displayBoardId; if (!resolvedBoardId) { setError("Select a board before creating an agent."); return; } setError(null); createAgentMutation.mutate({ data: { name: trimmed, board_id: resolvedBoardId, heartbeat_config: { every: heartbeatEvery.trim() || "10m", target: "last", includeReasoning: false, }, identity_profile: normalizeIdentityProfile( identityProfile, ) as unknown as Record | null, }, }); }; return (

Basic configuration

setName(event.target.value)} placeholder="e.g. Deploy bot" disabled={isLoading} />
setIdentityProfile((current) => ({ ...current, role: event.target.value, })) } placeholder="e.g. Founder, Social Media Manager" disabled={isLoading} />
{boards.length === 0 ? (

Create a board before adding agents.

) : null}

Personality & behavior

setIdentityProfile((current) => ({ ...current, communication_style: event.target.value, })) } disabled={isLoading} />

Schedule & notifications

setHeartbeatEvery(event.target.value)} placeholder="e.g. 10m" disabled={isLoading} />

How often this agent runs HEARTBEAT.md (10m, 30m, 2h).

{errorMessage ? (
{errorMessage}
) : null}
); }