this repo has no description
at main 195 lines 5.0 kB view raw
1/** 2 * Items tools for Letta agents 3 * 4 * Provides tools for managing tasks, brain dumps, and subtasks: 5 * - save_item: Save a new item to the database 6 * - update_item: Update an existing item's status, content, or priority 7 */ 8 9import { eq } from 'drizzle-orm'; 10import { db, schema } from '../db'; 11import { registerTool, type ToolDefinition } from './dispatcher'; 12 13/** 14 * Arguments for save_item tool 15 */ 16export interface SaveItemArgs { 17 /** Type of item to save */ 18 type: 'brain_dump' | 'task' | 'subtask'; 19 /** Item content */ 20 content: string; 21 /** Optional priority (0-4, default 2) */ 22 priority?: number; 23 /** Parent item ID for subtasks */ 24 parentId?: string; 25} 26 27/** 28 * Result from save_item tool 29 */ 30export interface SaveItemResult { 31 /** ID of saved item */ 32 id: string; 33 /** Success message */ 34 message: string; 35} 36 37/** 38 * Arguments for update_item tool 39 */ 40export interface UpdateItemArgs { 41 /** ID of item to update */ 42 id: string; 43 /** New status */ 44 status?: 'open' | 'in_progress' | 'done' | 'archived'; 45 /** Updated content */ 46 content?: string; 47 /** Updated priority (0-4) */ 48 priority?: number; 49} 50 51/** 52 * Result from update_item tool 53 */ 54export interface UpdateItemResult { 55 /** ID of updated item */ 56 id: string; 57 /** Success message */ 58 message: string; 59} 60 61/** 62 * save_item tool - Save a new task, brain dump, or subtask 63 */ 64export const saveItemTool: ToolDefinition<SaveItemArgs, SaveItemResult> = registerTool({ 65 name: 'save_item', 66 description: 'Save a new task, brain dump, or subtask to the database', 67 parameters: { 68 type: 'object', 69 properties: { 70 type: { 71 type: 'string', 72 enum: ['brain_dump', 'task', 'subtask'], 73 description: 'Type of item to save', 74 }, 75 content: { 76 type: 'string', 77 description: 'Item content', 78 }, 79 priority: { 80 type: 'integer', 81 minimum: 0, 82 maximum: 4, 83 description: 'Priority 0-4 (0=critical, 4=backlog, default 2=medium)', 84 }, 85 parentId: { 86 type: 'string', 87 description: 'Parent item ID for subtasks', 88 }, 89 }, 90 required: ['type', 'content'], 91 }, 92 handler: async (args, context) => { 93 // Generate a new UUID for the item 94 const id = crypto.randomUUID(); 95 96 // Validate subtask has parentId 97 if ( 98 args.type === 'subtask' && 99 (args.parentId === undefined || (typeof args.parentId === 'string' && args.parentId.length === 0)) 100 ) { 101 throw new Error('Subtasks must have a parentId'); 102 } 103 104 // Insert the item into the database 105 await db.insert(schema.items).values({ 106 id, 107 userId: context.userId, 108 type: args.type, 109 content: args.content, 110 status: 'open', 111 priority: args.priority ?? 2, 112 parentId: args.parentId ?? null, 113 }); 114 115 return { 116 id, 117 message: `Successfully saved ${args.type} with ID: ${id}`, 118 }; 119 }, 120}); 121 122/** 123 * update_item tool - Update an existing item 124 */ 125export const updateItemTool: ToolDefinition<UpdateItemArgs, UpdateItemResult> = registerTool({ 126 name: 'update_item', 127 description: "Update an existing item's status, content, or priority", 128 parameters: { 129 type: 'object', 130 properties: { 131 id: { 132 type: 'string', 133 description: 'Item ID to update', 134 }, 135 status: { 136 type: 'string', 137 enum: ['open', 'in_progress', 'done', 'archived'], 138 description: 'New status for the item', 139 }, 140 content: { 141 type: 'string', 142 description: 'Updated content', 143 }, 144 priority: { 145 type: 'integer', 146 minimum: 0, 147 maximum: 4, 148 description: 'Updated priority 0-4 (0=critical, 4=backlog)', 149 }, 150 }, 151 required: ['id'], 152 }, 153 handler: async (args, context) => { 154 // First, verify the item exists and belongs to the user 155 const existingItem = await db.query.items.findFirst({ 156 where: eq(schema.items.id, args.id), 157 }); 158 159 if (!existingItem) { 160 throw new Error(`Item with ID '${args.id}' not found`); 161 } 162 163 if (existingItem.userId !== context.userId) { 164 throw new Error(`Item with ID '${args.id}' does not belong to user ${String(context.userId)}`); 165 } 166 167 // Build the update object with only provided fields 168 const updates: { 169 status?: 'open' | 'in_progress' | 'done' | 'archived'; 170 content?: string; 171 priority?: number; 172 updatedAt?: Date; 173 } = { 174 updatedAt: new Date(), // Always update the timestamp 175 }; 176 177 if (args.status !== undefined) { 178 updates.status = args.status; 179 } 180 if (args.content !== undefined) { 181 updates.content = args.content; 182 } 183 if (args.priority !== undefined) { 184 updates.priority = args.priority; 185 } 186 187 // Update the item in the database 188 await db.update(schema.items).set(updates).where(eq(schema.items.id, args.id)); 189 190 return { 191 id: args.id, 192 message: `Successfully updated item ${args.id}`, 193 }; 194 }, 195});