diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-12 01:48:15 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-03-12 01:48:15 +0000 |
| commit | f28c22352aa1a8ede7552ee0277f7d60552d9094 (patch) | |
| tree | 02e9085809462e09b7d2a68ebb754e247eaecd22 /internal/executor | |
| parent | 4f83d35fa47bc71b31e0f92a0927bea8910c01b6 (diff) | |
feat: add Resume support for CANCELLED, FAILED, and BUDGET_EXCEEDED tasks
Interrupted tasks (CANCELLED, FAILED, BUDGET_EXCEEDED) now support session
resume in addition to restart. Both buttons are shown on the task card.
- executor: extend resumablePoolStates to include CANCELLED, FAILED, BUDGET_EXCEEDED
- api: extend handleResumeTimedOutTask to accept all resumable states with
state-specific resume messages; replace hard-coded TIMED_OUT check with a
resumableStates map
- web: add RESUME_STATES set; render Resume + Restart buttons for interrupted
states; TIMED_OUT keeps Resume only
- tests: 5 new Go tests (TestResumeInterrupted_*); updated task-actions.test.mjs
with 17 tests covering dual-button behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/executor')
| -rw-r--r-- | internal/executor/executor.go | 26 |
1 files changed, 23 insertions, 3 deletions
diff --git a/internal/executor/executor.go b/internal/executor/executor.go index 76c8ac7..bafacd2 100644 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -26,6 +26,8 @@ type Store interface { UpdateExecution(e *storage.Execution) error UpdateTaskState(id string, newState task.State) error UpdateTaskQuestion(taskID, questionJSON string) error + UpdateTaskSummary(taskID, summary string) error + AppendTaskInteraction(taskID string, interaction task.Interaction) error } // LogPather is an optional interface runners can implement to provide the log @@ -149,11 +151,20 @@ func (p *Pool) Cancel(taskID string) bool { return true } -// SubmitResume re-queues a blocked task using the provided resume execution. +// resumablePoolStates are the task states that may be submitted for session resume. +var resumablePoolStates = map[task.State]bool{ + task.StateBlocked: true, + task.StateTimedOut: true, + task.StateCancelled: true, + task.StateFailed: true, + task.StateBudgetExceeded: true, +} + +// SubmitResume re-queues a blocked or interrupted task using the provided resume execution. // The execution must have ResumeSessionID and ResumeAnswer set. func (p *Pool) SubmitResume(ctx context.Context, t *task.Task, exec *storage.Execution) error { - if t.State != task.StateBlocked && t.State != task.StateTimedOut { - return fmt.Errorf("task %s must be in BLOCKED or TIMED_OUT state to resume (current: %s)", t.ID, t.State) + if !resumablePoolStates[t.State] { + return fmt.Errorf("task %s must be in a resumable state to resume (current: %s)", t.ID, t.State) } if exec.ResumeSessionID == "" { return fmt.Errorf("resume execution for task %s must have a ResumeSessionID", t.ID) @@ -331,6 +342,15 @@ func (p *Pool) handleRunResult(ctx context.Context, t *task.Task, exec *storage. } } + summary := exec.Summary + if summary == "" && exec.StdoutPath != "" { + summary = extractSummary(exec.StdoutPath) + } + if summary != "" { + if summaryErr := p.store.UpdateTaskSummary(t.ID, summary); summaryErr != nil { + p.logger.Error("failed to update task summary", "taskID", t.ID, "error", summaryErr) + } + } if updateErr := p.store.UpdateExecution(exec); updateErr != nil { p.logger.Error("failed to update execution", "error", updateErr) } |
