'use client'; import { useState } from 'react'; import { useMutation, useQuery } from 'convex/react'; import { Bot } from 'lucide-react'; import { toast } from 'sonner'; import type { Doc, Id } from '@spoon/backend/convex/_generated/dataModel.js'; import { api } from '@spoon/backend/convex/_generated/api.js'; import { Button, Card, CardContent, CardHeader, CardTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, } from '@spoon/ui'; type AgentSettings = { defaultBaseBranch?: string; runtime?: 'opencode' | 'openai_direct'; agentModel: string; reasoningEffort: string; envFilePath?: string; customEnvFilePath?: string; materializeEnvFileByDefault?: boolean; aiProviderProfileId?: Id<'aiProviderProfiles'>; }; export const AgentRequestForm = ({ spoon, agentSettings, }: { spoon: Doc<'spoons'>; agentSettings?: AgentSettings | null; }) => { const secrets = useQuery(api.spoonSecrets.listForSpoon, { spoonId: spoon._id, }) ?? []; const profiles = useQuery(api.aiProviderProfiles.listMine, {})?.filter( (profile) => profile.enabled && profile.configured, ) ?? []; const defaultProfile = profiles.find((profile) => profile.isDefault); const createThread = useMutation(api.threads.createUserThread); const [prompt, setPrompt] = useState(''); const [baseBranch, setBaseBranch] = useState( agentSettings?.defaultBaseBranch ?? spoon.forkDefaultBranch ?? spoon.upstreamDefaultBranch, ); const [requestedBranchName, setRequestedBranchName] = useState(''); const [materializeEnvFile, setMaterializeEnvFile] = useState( agentSettings?.materializeEnvFileByDefault ?? false, ); const [envFilePath, setEnvFilePath] = useState( agentSettings?.envFilePath === 'custom' ? (agentSettings.customEnvFilePath ?? '.env.local') : (agentSettings?.envFilePath ?? '.env.local'), ); const [aiProviderProfileId, setAiProviderProfileId] = useState( agentSettings?.aiProviderProfileId ?? '__settings', ); const [submitting, setSubmitting] = useState(false); const effectiveProviderProfileId = aiProviderProfileId === '__settings' ? (agentSettings?.aiProviderProfileId ?? defaultProfile?._id) : aiProviderProfileId; const hasProvider = Boolean( effectiveProviderProfileId && profiles.some((profile) => profile._id === effectiveProviderProfileId), ); const selectedProfile = profiles.find((profile) => aiProviderProfileId === '__settings' ? profile._id === (agentSettings?.aiProviderProfileId ?? defaultProfile?._id) : profile._id === aiProviderProfileId, ); const submit = async (event: React.FormEvent) => { event.preventDefault(); setSubmitting(true); try { await createThread({ spoonId: spoon._id, prompt, baseBranch, requestedBranchName: requestedBranchName || undefined, materializeEnvFile, envFilePath: materializeEnvFile ? envFilePath : undefined, aiProviderProfileId: aiProviderProfileId === '__settings' ? undefined : (aiProviderProfileId as Id<'aiProviderProfiles'>), }); setPrompt(''); setRequestedBranchName(''); toast.success('Thread created.'); } catch (error) { console.error(error); toast.error('Could not queue agent job.'); } finally { setSubmitting(false); } }; return ( Request agent work