'use server' /** * Agent Server Actions * CRUD operations for agents and execution triggers. */ import { auth } from '@/auth' import { prisma } from '@/lib/prisma' import { revalidatePath } from 'next/cache' import { calculateNextRun } from '@/lib/agents/schedule' // --- CRUD --- export async function createAgent(data: { name: string description?: string type: string role: string sourceUrls?: string[] sourceNotebookId?: string sourceNoteIds?: string[] targetNotebookId?: string frequency?: string tools?: string[] maxSteps?: number notifyEmail?: boolean includeImages?: boolean scheduledTime?: string scheduledDay?: number timezone?: string slideTheme?: string slideStyle?: string }) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { const agent = await prisma.agent.create({ data: { name: data.name, description: data.description, type: data.type, role: data.role, sourceUrls: data.sourceUrls ? JSON.stringify(data.sourceUrls) : null, sourceNotebookId: data.sourceNotebookId || null, sourceNoteIds: data.sourceNoteIds ? JSON.stringify(data.sourceNoteIds) : null, targetNotebookId: data.targetNotebookId || null, frequency: data.frequency || 'manual', tools: data.tools ? JSON.stringify(data.tools) : '[]', maxSteps: data.maxSteps || 10, notifyEmail: data.notifyEmail || false, includeImages: data.includeImages || false, scheduledTime: data.scheduledTime || '08:00', scheduledDay: data.scheduledDay ?? null, timezone: data.timezone || null, slideTheme: data.slideTheme || null, slideStyle: data.slideStyle || null, userId: session.user.id, } }) // Calculate nextRun for scheduled agents const freq = data.frequency || 'manual' if (freq !== 'manual') { const nextRun = calculateNextRun({ frequency: freq, scheduledTime: data.scheduledTime || '08:00', scheduledDay: data.scheduledDay, timezone: data.timezone, }) if (nextRun) { await prisma.agent.update({ where: { id: agent.id }, data: { nextRun }, }) } } revalidatePath('/agents') return { success: true, agent } } catch (error) { console.error('Error creating agent:', error) throw new Error('Impossible de creer l\'agent') } } export async function updateAgent(id: string, data: { name?: string description?: string type?: string role?: string sourceUrls?: string[] sourceNotebookId?: string | null sourceNoteIds?: string[] | null targetNotebookId?: string | null frequency?: string isEnabled?: boolean tools?: string[] maxSteps?: number notifyEmail?: boolean includeImages?: boolean scheduledTime?: string scheduledDay?: number | null timezone?: string slideTheme?: string | null slideStyle?: string | null }) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { const existing = await prisma.agent.findUnique({ where: { id } }) if (!existing || existing.userId !== session.user.id) { throw new Error('Agent non trouve') } const updateData: Record = {} if (data.name !== undefined) updateData.name = data.name if (data.description !== undefined) updateData.description = data.description if (data.type !== undefined) updateData.type = data.type if (data.role !== undefined) updateData.role = data.role if (data.sourceUrls !== undefined) updateData.sourceUrls = JSON.stringify(data.sourceUrls) if (data.sourceNotebookId !== undefined) updateData.sourceNotebookId = data.sourceNotebookId if (data.sourceNoteIds !== undefined) updateData.sourceNoteIds = data.sourceNoteIds ? JSON.stringify(data.sourceNoteIds) : null if (data.targetNotebookId !== undefined) updateData.targetNotebookId = data.targetNotebookId if (data.frequency !== undefined) updateData.frequency = data.frequency if (data.isEnabled !== undefined) updateData.isEnabled = data.isEnabled if (data.tools !== undefined) updateData.tools = JSON.stringify(data.tools) if (data.maxSteps !== undefined) updateData.maxSteps = data.maxSteps if (data.notifyEmail !== undefined) updateData.notifyEmail = data.notifyEmail if (data.includeImages !== undefined) updateData.includeImages = data.includeImages if (data.scheduledTime !== undefined) updateData.scheduledTime = data.scheduledTime if (data.scheduledDay !== undefined) updateData.scheduledDay = data.scheduledDay if (data.timezone !== undefined) updateData.timezone = data.timezone if (data.slideTheme !== undefined) updateData.slideTheme = data.slideTheme if (data.slideStyle !== undefined) updateData.slideStyle = data.slideStyle // Recalculate nextRun when scheduling fields change const shouldRecalcNextRun = data.frequency !== undefined || data.scheduledTime !== undefined || data.scheduledDay !== undefined || data.timezone !== undefined if (shouldRecalcNextRun) { const freq = data.frequency || existing.frequency if (freq === 'manual') { updateData.nextRun = null } else { const nextRun = calculateNextRun({ frequency: freq, scheduledTime: data.scheduledTime || existing.scheduledTime || '08:00', scheduledDay: data.scheduledDay ?? existing.scheduledDay, timezone: data.timezone || existing.timezone, }) updateData.nextRun = nextRun } } const agent = await prisma.agent.update({ where: { id }, data: updateData }) revalidatePath('/agents') return { success: true, agent } } catch (error) { console.error('Error updating agent:', error) throw new Error('Impossible de mettre a jour l\'agent') } } export async function deleteAgent(id: string) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { const existing = await prisma.agent.findUnique({ where: { id } }) if (!existing || existing.userId !== session.user.id) { throw new Error('Agent non trouve') } await prisma.agent.delete({ where: { id } }) revalidatePath('/agents') return { success: true } } catch (error) { console.error('Error deleting agent:', error) throw new Error('Impossible de supprimer l\'agent') } } export async function getAgents() { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { const agents = await prisma.agent.findMany({ where: { userId: session.user.id, NOT: { frequency: 'one-shot' } }, include: { _count: { select: { actions: true } }, actions: { orderBy: { createdAt: 'desc' }, take: 1, }, notebook: { select: { id: true, name: true, icon: true } } }, orderBy: { createdAt: 'desc' } }) return agents } catch (error) { console.error('Error fetching agents:', error) throw new Error('Impossible de charger les agents') } } // --- Execution --- export async function runAgent(id: string) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } const userId = session.user.id // Verify ownership const agent = await prisma.agent.findUnique({ where: { id }, select: { id: true, userId: true } }) if (!agent || agent.userId !== userId) { return { success: false, agentId: id, error: 'Agent introuvable' } } // Fire and forget — return immediately so the UI doesn't block import('@/lib/ai/services/agent-executor.service') .then(({ executeAgent }) => executeAgent(id, userId)) .then(() => { /* revalidation is handled client-side via polling */ }) .catch(err => console.error('[runAgent] Background error:', err)) return { success: true, agentId: id, status: 'running' } } // --- History --- export async function getAgentActions(agentId: string) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { // Verify the agent belongs to the user const agent = await prisma.agent.findFirst({ where: { id: agentId, userId: session.user.id }, select: { id: true } }) if (!agent) throw new Error('Agent non trouve') const actions = await prisma.agentAction.findMany({ where: { agentId }, orderBy: { createdAt: 'desc' }, take: 20, select: { id: true, status: true, result: true, log: true, input: true, toolLog: true, tokensUsed: true, createdAt: true, } }) return actions } catch (error) { console.error('Error fetching agent actions:', error) throw new Error('Impossible de charger l\'historique') } } export async function deleteAgentHistory(agentId: string) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { const agent = await prisma.agent.findFirst({ where: { id: agentId, userId: session.user.id }, select: { id: true } }) if (!agent) throw new Error('Agent non trouve') await prisma.agentAction.deleteMany({ where: { agentId } }) revalidatePath('/agents') return { success: true } } catch (error) { console.error('Error deleting agent history:', error) throw new Error('Impossible de supprimer l\'historique') } } export async function toggleAgent(id: string, isEnabled: boolean) { const session = await auth() if (!session?.user?.id) { throw new Error('Non autorise') } try { const agent = await prisma.agent.update({ where: { id, userId: session.user.id }, data: { isEnabled } }) return { success: true, agent } } catch (error) { console.error('Error toggling agent:', error) throw new Error('Impossible de modifier l\'agent') } }