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

@@ -247,7 +247,9 @@ export async function processTaskIpc(
nextRun = scheduled.toISOString();
}
const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const taskId =
data.taskId ||
`task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const contextMode =
data.context_mode === 'group' || data.context_mode === 'isolated'
? data.context_mode
@@ -325,6 +327,70 @@ export async function processTaskIpc(
}
break;
case 'update_task':
if (data.taskId) {
const task = getTaskById(data.taskId);
if (!task) {
logger.warn(
{ taskId: data.taskId, sourceGroup },
'Task not found for update',
);
break;
}
if (!isMain && task.group_folder !== sourceGroup) {
logger.warn(
{ taskId: data.taskId, sourceGroup },
'Unauthorized task update attempt',
);
break;
}
const updates: Parameters<typeof updateTask>[1] = {};
if (data.prompt !== undefined) updates.prompt = data.prompt;
if (data.schedule_type !== undefined)
updates.schedule_type = data.schedule_type as
| 'cron'
| 'interval'
| 'once';
if (data.schedule_value !== undefined)
updates.schedule_value = data.schedule_value;
// Recompute next_run if schedule changed
if (data.schedule_type || data.schedule_value) {
const updatedTask = {
...task,
...updates,
};
if (updatedTask.schedule_type === 'cron') {
try {
const interval = CronExpressionParser.parse(
updatedTask.schedule_value,
{ tz: TIMEZONE },
);
updates.next_run = interval.next().toISOString();
} catch {
logger.warn(
{ taskId: data.taskId, value: updatedTask.schedule_value },
'Invalid cron in task update',
);
break;
}
} else if (updatedTask.schedule_type === 'interval') {
const ms = parseInt(updatedTask.schedule_value, 10);
if (!isNaN(ms) && ms > 0) {
updates.next_run = new Date(Date.now() + ms).toISOString();
}
}
}
updateTask(data.taskId, updates);
logger.info(
{ taskId: data.taskId, sourceGroup, updates },
'Task updated via IPC',
);
}
break;
case 'refresh_groups':
// Only main group can request a refresh
if (isMain) {