import { tool } from 'ai' import { z } from 'zod' import { toolRegistry } from './registry' import { prisma } from '@/lib/prisma' import { withAiProviderFallback } from '@/lib/ai/fallback' import { getSystemConfig } from '@/lib/config' toolRegistry.register({ name: 'task_extract', description: 'Extract action items (TODOs) from notes in a notebook. Reads all notes, identifies tasks with assignees and deadlines, and creates a synthesis note.', isInternal: true, buildTool: (ctx) => tool({ description: 'Extract action items from notes in a notebook. Creates a new note with all identified tasks.', inputSchema: z.object({ notebookId: z.string().optional().describe('Notebook ID to scan. If omitted, scans all user notes.'), noteIds: z.array(z.string()).optional().describe('Specific note IDs to scan instead of a whole notebook.'), locale: z.string().optional().describe('Language for the output (fr, en, es, de, etc.)'), }), execute: async ({ notebookId, noteIds, locale }) => { try { const where: any = { userId: ctx.userId, trashedAt: null } if (noteIds && noteIds.length > 0) { where.id = { in: noteIds } } else if (notebookId) { where.notebookId = notebookId } const notes = await prisma.note.findMany({ where, select: { id: true, title: true, content: true }, orderBy: { updatedAt: 'desc' }, take: 50, }) if (notes.length === 0) { return { error: 'No notes found to analyze' } } const notesContext = notes.map(n => `[ID: ${n.id}] "${n.title}":\n${(n.content || '').slice(0, 800)}` ).join('\n\n---\n\n') const lang = locale === 'fr' ? 'français' : locale === 'es' ? 'espagnol' : locale === 'de' ? 'allemand' : locale === 'it' ? 'italien' : locale === 'pt' ? 'portugais' : locale === 'nl' ? 'néerlandais' : locale === 'ru' ? 'russe' : locale === 'zh' ? 'chinois' : locale === 'ja' ? 'japonais' : locale === 'ar' ? 'arabe' : locale === 'fa' ? 'persan' : locale === 'hi' ? 'hindi' : 'English' const config = await getSystemConfig() const prompt = `You are a task extraction specialist. Analyze the following notes and extract ALL action items, tasks, and TODOs. For each task identified, provide: - **Task**: Clear, actionable description - **Source**: The note title where it was found - **Assignee**: If mentioned (otherwise "Unassigned") - **Deadline**: If mentioned (otherwise "No deadline") - **Priority**: High/Medium/Low based on urgency signals in the text - **Status**: If already completed or in-progress based on context NOTES TO ANALYZE: ${notesContext} Respond in ${lang}. Structure the output as a clean Markdown document with: 1. A summary paragraph 2. Tasks grouped by priority (High → Medium → Low) 3. A summary table at the end Format each task as: ### [Priority] Task Title - **Description**: ... - **Source note**: ... - **Assignee**: ... - **Deadline**: ... - **Status**: ...` const result = await withAiProviderFallback('tags', config, (provider) => provider.generateText(prompt) ) const summaryTitle = locale === 'fr' ? `Action Items — ${new Date().toLocaleDateString('fr-FR')}` : `Action Items — ${new Date().toLocaleDateString('en-US')}` const createdNote = await prisma.note.create({ data: { title: summaryTitle, content: result, type: 'markdown', isMarkdown: true, autoGenerated: true, userId: ctx.userId, notebookId: notebookId || null, }, select: { id: true, title: true }, }) return { success: true, noteId: createdNote.id, title: createdNote.title, notesAnalyzed: notes.length, tasksNoteUrl: `/notes/${createdNote.id}`, } } catch (e: any) { return { error: `Task extraction failed: ${e.message}` } } }, }), })