Merge branch 'claudedo/16172942c1424a67a0499e01cefb7d60'

This commit is contained in:
mika kuns
2026-08-10 15:08:51 +02:00
19 changed files with 1295 additions and 24 deletions
+27 -7
View File
@@ -158,12 +158,14 @@ public sealed class TaskRunner
}
else
{
await MarkFailed(task.Id, task.Title, slot, retryResult.ErrorMarkdown, retryResult.TurnCount);
await MarkFailed(task.Id, task.Title, slot, retryResult.ErrorMarkdown, retryResult.TurnCount,
retryConfig.MaxTurns, ClassifyFailureReason(retryResult.TerminalReason));
}
}
else
{
await MarkFailed(task.Id, task.Title, slot, result.ErrorMarkdown, result.TurnCount);
await MarkFailed(task.Id, task.Title, slot, result.ErrorMarkdown, result.TurnCount,
resolvedConfig.MaxTurns, ClassifyFailureReason(result.TerminalReason));
}
}
@@ -172,7 +174,7 @@ public sealed class TaskRunner
catch (OperationCanceledException)
{
_logger.LogInformation("Task {TaskId} was cancelled", task.Id);
await MarkFailed(task.Id, task.Title, slot, "Task cancelled.");
await MarkFailed(task.Id, task.Title, slot, "Task cancelled.", failureReason: "cancelled");
}
catch (Exception ex)
{
@@ -270,7 +272,8 @@ public sealed class TaskRunner
}
else
{
await MarkFailed(taskId, task.Title, slot, result.ErrorMarkdown, result.TurnCount);
await MarkFailed(taskId, task.Title, slot, result.ErrorMarkdown, result.TurnCount,
resolvedConfig.MaxTurns, ClassifyFailureReason(result.TerminalReason));
}
await _broadcaster.TaskUpdated(taskId);
@@ -278,7 +281,7 @@ public sealed class TaskRunner
catch (OperationCanceledException)
{
_logger.LogInformation("Task {TaskId} was cancelled during continue", taskId);
await MarkFailed(taskId, task.Title, slot, "Task cancelled.");
await MarkFailed(taskId, task.Title, slot, "Task cancelled.", failureReason: "cancelled");
}
catch (Exception ex)
{
@@ -404,6 +407,9 @@ public sealed class TaskRunner
run.ErrorMarkdown = result.ErrorMarkdown;
run.ExitCode = result.ExitCode;
run.TurnCount = result.TurnCount;
run.ResultSubtype = result.ResultSubtype;
run.TerminalReason = result.TerminalReason;
run.Errors = result.Errors.Count > 0 ? string.Join("\n", result.Errors) : null;
if (result.SessionId is not null)
await ApplyUsageAsync(run, taskId, result.SessionId);
run.FinishedAt = DateTime.UtcNow;
@@ -421,6 +427,7 @@ public sealed class TaskRunner
// Ensure the run row is completed so ContinueAsync / inspection
// isn't left staring at a null session_id / finished_at.
run.ErrorMarkdown = "Cancelled.";
run.TerminalReason = "cancelled";
run.ExitCode = -1;
run.FinishedAt = DateTime.UtcNow;
try
@@ -550,14 +557,17 @@ public sealed class TaskRunner
task.Id, result.TurnCount, result.TokensIn, result.TokensOut);
}
private async Task MarkFailed(string taskId, string taskTitle, string slot, string? error, int turnCount = 0)
private async Task MarkFailed(
string taskId, string taskTitle, string slot, string? error, int turnCount = 0,
int? maxTurns = null, string failureReason = "error")
{
// Terminal write for a failed task: never cancel (the status must always
// be persisted) and never throw (a logging failure must not mask the error).
try
{
var finishedAt = DateTime.UtcNow;
await _state.FailAsync(taskId, finishedAt, error, CancellationToken.None);
await _state.FailAsync(taskId, finishedAt, error, CancellationToken.None,
failureReason, turnCount > 0 ? turnCount : null, maxTurns);
await _broadcaster.WorkerLog($"Finished \"{taskTitle}\" (failed)", WorkerLogLevel.Error, DateTime.UtcNow);
await _broadcaster.TaskFinished(slot, taskId, "failed", finishedAt);
_logger.LogWarning("Task {TaskId} failed (turns={Turns}): {Error}", taskId, turnCount, error);
@@ -568,6 +578,16 @@ public sealed class TaskRunner
}
}
/// <summary>Classifies the CLI's raw <c>terminal_reason</c> into the small, MCP-facing enum
/// (<c>max_turns|timeout|error</c>) get_task/batch_get_tasks report as failureReason.
/// "cancelled" is set explicitly at the call sites that know it (there's no CLI signal for it).</summary>
internal static string ClassifyFailureReason(string? terminalReason) => terminalReason switch
{
"max_turns" => "max_turns",
"timeout" => "timeout",
_ => "error",
};
private string BuildRunMcpConfigJson(string token)
{
var payload = new