feat: add update_task tool and return task ID from schedule_task

schedule_task was creating duplicate tasks when users asked to modify
a schedule, because the agent had no way to update an existing task
and didn't know the ID of the task it created. Now schedule_task
generates and returns the task ID, and a new update_task tool allows
modifying prompt, schedule_type, and schedule_value in place.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gavriel Cohen
2026-03-06 12:15:36 +02:00
parent e14c8580af
commit 68123fdd81
2 changed files with 123 additions and 4 deletions

View File

@@ -64,7 +64,7 @@ server.tool(
server.tool(
'schedule_task',
`Schedule a recurring or one-time task. The task will run as a full agent with access to all tools.
`Schedule a recurring or one-time task. The task will run as a full agent with access to all tools. Returns the task ID for future reference. To modify an existing task, use update_task instead.
CONTEXT MODE - Choose based on task type:
\u2022 "group": Task runs in the group's conversation context, with access to chat history. Use for tasks that need context about ongoing discussions, user preferences, or recent interactions.
@@ -130,8 +130,11 @@ SCHEDULE VALUE FORMAT (all times are LOCAL timezone):
// Non-main groups can only schedule for themselves
const targetJid = isMain && args.target_group_jid ? args.target_group_jid : chatJid;
const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const data = {
type: 'schedule_task',
taskId,
prompt: args.prompt,
schedule_type: args.schedule_type,
schedule_value: args.schedule_value,
@@ -141,10 +144,10 @@ SCHEDULE VALUE FORMAT (all times are LOCAL timezone):
timestamp: new Date().toISOString(),
};
const filename = writeIpcFile(TASKS_DIR, data);
writeIpcFile(TASKS_DIR, data);
return {
content: [{ type: 'text' as const, text: `Task scheduled (${filename}): ${args.schedule_type} - ${args.schedule_value}` }],
content: [{ type: 'text' as const, text: `Task ${taskId} scheduled: ${args.schedule_type} - ${args.schedule_value}` }],
};
},
);
@@ -244,6 +247,56 @@ server.tool(
},
);
server.tool(
'update_task',
'Update an existing scheduled task. Only provided fields are changed; omitted fields stay the same.',
{
task_id: z.string().describe('The task ID to update'),
prompt: z.string().optional().describe('New prompt for the task'),
schedule_type: z.enum(['cron', 'interval', 'once']).optional().describe('New schedule type'),
schedule_value: z.string().optional().describe('New schedule value (see schedule_task for format)'),
},
async (args) => {
// Validate schedule_value if provided
if (args.schedule_type === 'cron' || (!args.schedule_type && args.schedule_value)) {
if (args.schedule_value) {
try {
CronExpressionParser.parse(args.schedule_value);
} catch {
return {
content: [{ type: 'text' as const, text: `Invalid cron: "${args.schedule_value}".` }],
isError: true,
};
}
}
}
if (args.schedule_type === 'interval' && args.schedule_value) {
const ms = parseInt(args.schedule_value, 10);
if (isNaN(ms) || ms <= 0) {
return {
content: [{ type: 'text' as const, text: `Invalid interval: "${args.schedule_value}".` }],
isError: true,
};
}
}
const data: Record<string, string | undefined> = {
type: 'update_task',
taskId: args.task_id,
groupFolder,
isMain: String(isMain),
timestamp: new Date().toISOString(),
};
if (args.prompt !== undefined) data.prompt = args.prompt;
if (args.schedule_type !== undefined) data.schedule_type = args.schedule_type;
if (args.schedule_value !== undefined) data.schedule_value = args.schedule_value;
writeIpcFile(TASKS_DIR, data);
return { content: [{ type: 'text' as const, text: `Task ${args.task_id} update requested.` }] };
},
);
server.tool(
'register_group',
`Register a new chat/group so the agent can respond to messages there. Main group only.