/** * update_task tool * * Agent tool to update a task's status. * Auto-sets started_at when transitioning to "in_progress". * Auto-sets finish_at when transitioning to "done", "failed", or "removed". * * Used by: mission-orchestrator skill */ import { readState, readRun, writeRun, findTask, updateTaskInRun, getTimestamp, TaskStatus } from "../state.js"; export interface UpdateTaskParams { task_id: string; status: TaskStatus; } export interface UpdateTaskResult { success: boolean; message: string; taskId: string | null; phaseId: string | null; previousStatus: TaskStatus | null; newStatus: TaskStatus | null; startedAt: string | null; finishAt: string | null; errors: string[]; } /** * Update a task's status in the active mission run * * @param params.task_id - ID of the task to update (e.g., "phase1-task1") * @param params.status - New status ("pending" | "in_progress" | "done" | "failed" | "removed") * @returns Result with updated task info */ export function updateTask(params: UpdateTaskParams): UpdateTaskResult { const result: UpdateTaskResult = { success: false, message: "", taskId: null, phaseId: null, previousStatus: null, newStatus: null, startedAt: null, finishAt: null, errors: [] }; try { // Validate parameters if (!params.task_id || params.task_id.trim() === "") { result.errors.push("Task ID is required"); result.message = "Failed to update task: task_id is required"; return result; } if (!params.status) { result.errors.push("Status is required"); result.message = "Failed to update task: status is required"; return result; } const validStatuses: TaskStatus[] = ["pending", "in_progress", "done", "failed", "removed"]; if (!validStatuses.includes(params.status)) { result.errors.push(`Invalid status: ${params.status}. Must be one of: ${validStatuses.join(", ")}`); result.message = "Failed to update task: invalid status"; return result; } // Get active run const state = readState(); if (!state.active_run_id) { result.errors.push("No active run"); result.message = "Failed to update task: no active mission run"; return result; } // Read run.json const run = readRun(state.active_run_id); if (!run) { result.errors.push(`Run not found: ${state.active_run_id}`); result.message = "Failed to update task: run not found"; return result; } // Find task const taskInfo = findTask(run, params.task_id.trim()); if (!taskInfo) { result.errors.push(`Task not found: ${params.task_id}`); result.message = "Failed to update task: task not found"; return result; } const { task, phase } = taskInfo; // Capture previous status result.previousStatus = task.status; result.taskId = task.id; result.phaseId = phase.id; // Calculate timestamp updates const updates: Partial = { status: params.status }; const now = getTimestamp(); // Set started_at when transitioning to in_progress if (params.status === "in_progress" && task.status !== "in_progress") { updates.started_at = now; result.startedAt = now; } // Set finish_at when transitioning to terminal states const terminalStatuses: TaskStatus[] = ["done", "failed", "removed"]; if (terminalStatuses.includes(params.status) && !terminalStatuses.includes(task.status)) { updates.finish_at = now; result.finishAt = now; } // Update run const updatedRun = updateTaskInRun(run, task.id, updates); writeRun(updatedRun); result.success = true; result.newStatus = params.status; result.message = `Task ${task.id} updated: ${task.status} → ${params.status}`; } catch (error) { result.success = false; result.message = `Error updating task: ${error instanceof Error ? error.message : String(error)}`; result.errors.push(String(error)); } return result; }