diff --git a/src/ClaudeDo.Worker/Services/QueueService.cs b/src/ClaudeDo.Worker/Services/QueueService.cs index 19f8bed..e3912c5 100644 --- a/src/ClaudeDo.Worker/Services/QueueService.cs +++ b/src/ClaudeDo.Worker/Services/QueueService.cs @@ -75,6 +75,31 @@ public sealed class QueueService : BackgroundService } } + public async Task ContinueTask(string taskId, string followUpPrompt) + { + var task = await _taskRepo.GetByIdAsync(taskId) + ?? throw new KeyNotFoundException($"Task '{taskId}' not found."); + + if (task.Status == Data.Models.TaskStatus.Running) + throw new InvalidOperationException("Task is currently running."); + + lock (_lock) + { + if (_overrideSlot is not null) + throw new InvalidOperationException("override slot busy"); + + var cts = new CancellationTokenSource(); + _overrideSlot = new QueueSlotState { TaskId = taskId, StartedAt = DateTime.UtcNow, Cts = cts }; + + _ = RunContinueInSlotAsync(taskId, followUpPrompt, cts.Token).ContinueWith(_ => + { + lock (_lock) { _overrideSlot = null; } + }, TaskScheduler.Default); + } + + return taskId; + } + public bool CancelTask(string taskId) { lock (_lock) @@ -159,4 +184,17 @@ public sealed class QueueService : BackgroundService _logger.LogError(ex, "Slot runner error for task {TaskId}", task.Id); } } + + private async Task RunContinueInSlotAsync(string taskId, string followUpPrompt, CancellationToken ct) + { + try + { + _logger.LogInformation("Continuing task {TaskId} in override slot", taskId); + await _runner.ContinueAsync(taskId, followUpPrompt, "override", ct); + } + catch (Exception ex) + { + _logger.LogError(ex, "Continue runner error for task {TaskId}", taskId); + } + } }