/** * Note Creation Tool * * This tool allows the LLM to create new notes in Trilium. */ import type { Tool, ToolHandler } from './tool_interfaces.js'; import log from '../../log.js'; import becca from '../../../becca/becca.js'; import notes from '../../notes.js'; import attributes from '../../attributes.js'; /** * Definition of the note creation tool */ export const noteCreationToolDefinition: Tool = { type: 'function', function: { name: 'create_note', description: 'Create a new note in Trilium with the specified content and attributes', parameters: { type: 'object', properties: { parentNoteId: { type: 'string', description: 'ID of the parent note under which to create the new note. If not specified, creates under root.' }, title: { type: 'string', description: 'Title of the new note' }, content: { type: 'string', description: 'Content of the new note' }, type: { type: 'string', description: 'Type of the note (text, code, etc.)', enum: ['text', 'code', 'file', 'image', 'search', 'relation-map', 'book', 'mermaid', 'canvas'] }, mime: { type: 'string', description: 'MIME type of the note (e.g., text/html, application/json). Only required for certain note types.' }, attributes: { type: 'array', description: 'Array of attributes to set on the note (e.g., [{"name":"#tag"}, {"name":"priority", "value":"high"}])', items: { type: 'object', properties: { name: { type: 'string', description: 'Name of the attribute' }, value: { type: 'string', description: 'Value of the attribute (optional)' } }, required: ['name'] } } }, required: ['title', 'content'] } } }; /** * Note creation tool implementation */ export class NoteCreationTool implements ToolHandler { public definition: Tool = noteCreationToolDefinition; /** * Execute the note creation tool */ public async execute(args: { parentNoteId?: string, title: string, content: string, type?: string, mime?: string, attributes?: Array<{ name: string, value?: string }> }): Promise { try { const { parentNoteId, title, content, type = 'text', mime } = args; log.info(`Executing create_note tool - Title: "${title}", Type: ${type}, ParentNoteId: ${parentNoteId || 'root'}`); // Validate parent note exists if specified let parent = null; if (parentNoteId) { parent = becca.notes[parentNoteId]; if (!parent) { return `Error: Parent note with ID ${parentNoteId} not found. Please specify a valid parent note ID.`; } } else { // Use root note if no parent specified parent = becca.getNote('root'); } // Make sure we have a valid parent at this point if (!parent) { return 'Error: Failed to get a valid parent note. Root note may not be accessible.'; } // Determine the appropriate mime type let noteMime = mime; if (!noteMime) { // Set default mime types based on note type switch (type) { case 'text': noteMime = 'text/html'; break; case 'code': noteMime = 'text/plain'; break; case 'file': noteMime = 'application/octet-stream'; break; case 'image': noteMime = 'image/png'; break; default: noteMime = 'text/html'; } } // Create the note const createStartTime = Date.now(); const result = notes.createNewNote({ parentNoteId: parent.noteId, title: title, content: content, type: type as any, // Cast as any since not all string values may match the exact NoteType union mime: noteMime }); const noteId = result.note.noteId; const createDuration = Date.now() - createStartTime; if (!noteId) { return 'Error: Failed to create note. An unknown error occurred.'; } log.info(`Note created successfully in ${createDuration}ms, ID: ${noteId}`); // Add attributes if specified if (args.attributes && args.attributes.length > 0) { log.info(`Adding ${args.attributes.length} attributes to the note`); for (const attr of args.attributes) { if (!attr.name) continue; const attrStartTime = Date.now(); // Use createLabel for label attributes if (attr.name.startsWith('#') || attr.name.startsWith('~')) { await attributes.createLabel(noteId, attr.name.substring(1), attr.value || ''); } else { // Use createRelation for relation attributes if value looks like a note ID if (attr.value && attr.value.match(/^[a-zA-Z0-9_]{12}$/)) { await attributes.createRelation(noteId, attr.name, attr.value); } else { // Default to label for other attributes await attributes.createLabel(noteId, attr.name, attr.value || ''); } } const attrDuration = Date.now() - attrStartTime; log.info(`Added attribute ${attr.name}=${attr.value || ''} in ${attrDuration}ms`); } } // Return the new note's information const newNote = becca.notes[noteId]; return { success: true, noteId: noteId, title: newNote.title, type: newNote.type, message: `Note "${title}" created successfully` }; } catch (error: any) { log.error(`Error executing create_note tool: ${error.message || String(error)}`); return `Error: ${error.message || String(error)}`; } } }