this repo has no description
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});