Files
Momento/memento-note/app/actions/agent-actions.ts
sepehr f0999263a0
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 42s
fix: remove nextRun recalculation from getAgents() that blocked cron
The getAgents() function was recalculating nextRun to future dates when
it found past values. This prevented the cron scheduler from ever
finding due agents (nextRun <= now was always false since getAgents
had already pushed it to the future).

Also fix toast polling: use null sentinel for agents without initial
actions so first execution is still detected. Limit cron to 3 agents
per cycle and add logging.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 13:00:55 +02:00

288 lines
8.1 KiB
TypeScript

'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
targetNotebookId?: string
frequency?: string
tools?: string[]
maxSteps?: number
notifyEmail?: boolean
includeImages?: boolean
scheduledTime?: string
scheduledDay?: number
timezone?: 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,
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,
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
targetNotebookId?: string | null
frequency?: string
isEnabled?: boolean
tools?: string[]
maxSteps?: number
notifyEmail?: boolean
includeImages?: boolean
scheduledTime?: string
scheduledDay?: number | null
timezone?: 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')
}
const updateData: Record<string, unknown> = {}
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.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
// 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 },
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')
}
try {
const { executeAgent } = await import('@/lib/ai/services/agent-executor.service')
const result = await executeAgent(id, session.user.id)
revalidatePath('/agents')
revalidatePath('/')
return result
} catch (error) {
console.error('Error running agent:', error)
return {
success: false,
actionId: '',
error: error instanceof Error ? error.message : 'Erreur inconnue'
}
}
}
// --- History ---
export async function getAgentActions(agentId: string) {
const session = await auth()
if (!session?.user?.id) {
throw new Error('Non autorise')
}
try {
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 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 },
data: { isEnabled }
})
return { success: true, agent }
} catch (error) {
console.error('Error toggling agent:', error)
throw new Error('Impossible de modifier l\'agent')
}
}