/* Agents — spawn a new agent into a repository and manage the ones already running. * Spawning enqueues a control command that the worker turns into a tmux + claude process. */ "use client"; import { Fragment, useMemo, useState } from "react"; import { useDashboard } from "@/components/store"; import { Badge, Button, Card, Input, Select, StatusBadge, Textarea } from "@/components/ui"; import { fmtFull, timeAgo } from "@/lib/format"; const ROLE_OPTS = [ { value: "", label: "Role — none" }, { value: "junior", label: "junior" }, { value: "senior", label: "senior" }, { value: "deploy", label: "deploy" }, ]; const PLACEMENT_OPTS = [ { value: "worktree", label: "git worktree on branch" }, { value: "subdir", label: "subdir under root" }, ]; const emptySpawn = { name: "", role: "", placement: "worktree" as "worktree" | "subdir", worktree: "", subdir: "", task: "", model_id: "", }; export function AgentsSection() { const s = useDashboard(); const [form, setForm] = useState(emptySpawn); const projectOpts = useMemo( () => s.projects.map((p) => ({ value: p.id, label: p.id })), [s.projects], ); /* The seamless switch: Claude subscription by default, plus every enabled backend * registered on the Claude page's Models tab. Same binary, hooks, and skills either * way — only the ANTHROPIC_* env of the launched process differs. */ const modelOpts = useMemo( () => [ { value: "", label: "Claude (subscription)" }, ...s.claudeModels .filter((m) => m.enabled) .map((m) => ({ value: String(m.id), label: `${m.name} (${m.model})` })), ], [s.claudeModels], ); const modelName = useMemo(() => { const byId = new Map(s.claudeModels.map((m) => [m.id, m.name])); return (id: number | null | undefined) => (id == null ? null : byId.get(id) ?? `#${id}`); }, [s.claudeModels]); const agents = useMemo( () => s.agents.filter((a) => a.project_id === s.selectedProjectId), [s.agents, s.selectedProjectId], ); const spawn = async () => { const ok = await s.spawnAgent(form); if (ok) setForm(emptySpawn); }; return ( <>
Agents
Spawn agents into a repository and manage running sessions.
{s.projects.length === 0 ? (
Register a repository first.
) : ( <>
setForm({ ...form, name: v })} placeholder="junior" /> setForm({ ...form, placement: v as "worktree" | "subdir" })} options={PLACEMENT_OPTS} /> {form.placement === "worktree" ? ( setForm({ ...form, worktree: v })} placeholder="feat/auth" /> ) : ( setForm({ ...form, subdir: v })} placeholder="api" /> )}