From 6ac15be438e3692cbc2ae2f36ab2d69468fc6372 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 14 Mar 2026 00:33:46 +0000 Subject: fix: cancel blocked tasks + auto-complete completion reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes for BLOCKED task issues: 1. Allow BLOCKED → CANCELLED state transition so users can cancel tasks stuck waiting for input. Adds Cancel button to BLOCKED task cards in the UI alongside the question/answer controls. 2. Detect when agents write completion reports to $CLAUDOMATOR_QUESTION_FILE instead of real questions. If the question JSON has no options and no "?" in the text, treat it as a summary (stored on the execution) and fall through to normal completion + sandbox teardown rather than blocking. Also tightened the preamble to make the distinction explicit. Co-Authored-By: Claude Sonnet 4.6 --- web/app.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'web/app.js') diff --git a/web/app.js b/web/app.js index afb6a75..ad60f34 100644 --- a/web/app.js +++ b/web/app.js @@ -164,6 +164,14 @@ function createTaskCard(task) { footer.appendChild(rejectBtn); } else if (task.state === 'BLOCKED') { renderQuestionFooter(task, footer); + const cancelBtn = document.createElement('button'); + cancelBtn.className = 'btn-cancel'; + cancelBtn.textContent = 'Cancel'; + cancelBtn.addEventListener('click', (e) => { + e.stopPropagation(); + handleCancel(task.id, cancelBtn, footer); + }); + footer.appendChild(cancelBtn); } else if (RESUME_STATES.has(task.state)) { const resumeBtn = document.createElement('button'); resumeBtn.className = 'btn-resume'; @@ -869,8 +877,11 @@ async function handleReject(taskId, btn, footer) { // ── Start-next-task ───────────────────────────────────────────────────────────── -async function startNextTask() { - const res = await fetch(`${API_BASE}/api/scripts/start-next-task`, { method: 'POST' }); +async function startNextTask(agent) { + const url = agent && agent !== 'auto' + ? `${API_BASE}/api/scripts/start-next-task?agent=${agent}` + : `${API_BASE}/api/scripts/start-next-task`; + const res = await fetch(url, { method: 'POST' }); if (!res.ok) { let msg = `HTTP ${res.status}`; try { const body = await res.json(); msg = body.error || msg; } catch {} @@ -880,10 +891,12 @@ async function startNextTask() { } async function handleStartNextTask(btn) { + const agentSelector = document.getElementById('select-agent'); + const agent = agentSelector ? agentSelector.value : 'auto'; btn.disabled = true; btn.textContent = 'Starting…'; try { - const result = await startNextTask(); + const result = await startNextTask(agent); const output = (result.output || '').trim(); btn.textContent = output || 'No task to start'; setTimeout(() => { btn.textContent = 'Start Next'; btn.disabled = false; }, 3000); -- cgit v1.2.3