/** New Task page: one prompt box with the agent and its settings; where it works under it. */ import clsx from "clsx"; import { useEffect, useState } from "react"; import type { AgentProbe, Branch } from "@/bindings"; import { executeAction } from "@/platform/actions"; import { call, commands } from "@/platform/ipc"; import { useTx, useTxTemplate } from "@/platform/i18n"; import { basename, useProject } from "@/platform/project"; import { Icon, Select, T, type SelectOption } from "@/ui"; import { defaultTaskProject } from "./project"; import { maybeEditorService, type EditorInput } from "@/workbench/contributions"; import { Composer } from "./Composer"; import { SubmitButton } from "./SubmitButton"; import { canSend, emptyDraft } from "./composerModel"; import { ConfigPickers } from "./ConfigPickers"; import { providerIcon } from "./model"; import { ADD_AGENT, providerOptions as agentOptions } from "./providerOptions"; import { HOME, NO_COMMANDS, refreshProviders, startTask, updateDraft, useAgentCommands, useAgents } from "./store"; import styles from "./Home.module.css"; export function HomeEditor({ docked = false }: { docked?: boolean; input?: EditorInput }) { const tx = useTx(); const txTemplate = useTxTemplate(); const draft = useAgents((s) => s.draft); const providers = useAgents((s) => s.providers); const history = useAgents((s) => s.history); const composer = useAgents((s) => s.composers.get(HOME)) ?? emptyDraft(); const project = useProject((s) => s.project); const [selected, setSelected] = useState(() => defaultTaskProject(project, maybeEditorService()?.activePath() ?? null, draft.projectRoot), ); const members = project?.workspace?.projects ?? []; const root = members.includes(selected ?? "") ? selected! : (members[0] ?? project?.root ?? ""); const [repoState, setRepoState] = useState<{ root: string; branches: Branch[]; current: string | null; isGit: boolean; } | null>(null); const branches = repoState?.root === root ? repoState.branches : []; const current = repoState?.root === root ? repoState.current : null; const [error, setError] = useState(null); const [starting, setStarting] = useState(false); // Agents installed in Settings (its own window) show up when this one is back in front. useEffect(() => { const refresh = () => void refreshProviders(); window.addEventListener("focus", refresh); return () => window.removeEventListener("focus", refresh); }, []); useEffect(() => { let live = true; const repo = project?.workspace ? root : null; void Promise.all([call(commands.vcsBranchInfo(repo)), call(commands.vcsBranches(repo))]) .then(([info, list]) => { if (!live) return; setRepoState({ root, branches: list.filter((b) => !b.remote), current: info.head, isGit: true }); }) .catch(() => { if (live) setRepoState({ root, branches: [], current: null, isGit: false }); }); return () => { live = false; }; }, [root, project?.workspace]); const provider = providers.find((p) => p.id === draft.provider) ?? providers[0]; const probe = useProbe(provider?.installed ? provider.id : null); const agentCommands = useAgentCommands(provider?.id, null, provider?.commands ?? NO_COMMANDS); const providerOptions: SelectOption[] = agentOptions(providers, tx, providerIcon); // Without git under the project there are no worktrees: force the local checkout. const isGit = project?.workspace ? repoState?.root === root && repoState.isGit : (project?.isGit ?? false); const isolation = isGit ? draft.isolation : "local"; const base = draft.base ?? current; const canStart = canSend(composer) && !!provider?.installed && !starting && (!project?.workspace || repoState?.root === root); const start = () => { if (!canStart) return; setStarting(true); setError(null); startTask(isolation, project?.workspace ? root : null) .catch((e: unknown) => setError(e instanceof Error ? e.message : String(e))) .finally(() => setStarting(false)); }; const setOption = (id: string, value: string) => updateDraft({ config: { ...draft.config, [id]: value } }); return (
{!docked && (

What should the agent do?

)} ({ value: path, label: basename(path), detail: path }))} onChange={(path) => { setSelected(path); updateDraft({ base: null }); }} /> ) : ( {members.length ? basename(root) : project?.name} )} {isGit && ( ({ value: b.name, label: b.name }))} onChange={(b) => updateDraft({ base: b === current ? null : b })} /> )}
{probe && (!docked || probe.error) && (

{probe.error ? txTemplate(`${provider?.name ?? probe.provider} didn't start: ${probe.error}`) : [probe.name, probe.version, probe.protocol].filter(Boolean).join(" · ")}

)} {(error || (provider && !provider.installed) || (provider && !provider.config.length)) && (

{error ?? (provider && !provider.installed ? ( txTemplate(`${provider.command} was not found on PATH`) ) : ( Models and modes appear after this agent's first task. ))}

)}
); } /** What the agent reports about itself when it starts (no session is opened), once per agent. */ function useProbe(provider: string | null): AgentProbe | null { const [probe, setProbe] = useState(null); useEffect(() => { if (!provider) return; let live = true; call(commands.agentProbe(provider, false)) .then((p) => live && setProbe(p)) .catch(() => {}); return () => { live = false; }; }, [provider]); return probe?.provider === provider ? probe : null; }