feat: add handoff workflow checkpoint recovery

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-04-01 19:28:05 +02:00
co-authored by Copilot
parent 1ceb3d5669
commit 13bcc44f1a
10 changed files with 761 additions and 24 deletions
@@ -1,3 +1,4 @@
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using Aryx.AgentHost.Contracts;
@@ -798,6 +799,50 @@ public sealed class CopilotWorkflowRunnerTests
});
}
[Fact]
public async Task HandleWorkflowEventAsync_EmitsWorkflowCheckpointSavedEvent()
{
RunTurnCommandDto command = CreateHandoffCommand();
CopilotTurnExecutionState state = new(command);
List<WorkflowCheckpointSavedEventDto> checkpoints = [];
MethodInfo handleWorkflowEvent = typeof(CopilotWorkflowRunner).GetMethod(
"HandleWorkflowEventAsync",
BindingFlags.NonPublic | BindingFlags.Static)!;
Task<bool> handleTask = (Task<bool>)handleWorkflowEvent.Invoke(
null,
[
command,
new SuperStepCompletedEvent(
3,
new SuperStepCompletionInfo([])
{
Checkpoint = new CheckpointInfo(command.RequestId, "checkpoint-1"),
}),
Array.Empty<ChatMessage>(),
state,
(Func<TurnDeltaEventDto, Task>)(_ => Task.CompletedTask),
(Func<SidecarEventDto, Task>)(sidecarEvent =>
{
checkpoints.Add(Assert.IsType<WorkflowCheckpointSavedEventDto>(sidecarEvent));
return Task.CompletedTask;
}),
])!;
bool shouldEndTurn = await handleTask;
Assert.False(shouldEndTurn);
WorkflowCheckpointSavedEventDto checkpoint = Assert.Single(checkpoints);
Assert.Equal("workflow-checkpoint-saved", checkpoint.Type);
Assert.Equal(command.SessionId, checkpoint.SessionId);
Assert.Equal(command.RequestId, checkpoint.WorkflowSessionId);
Assert.Equal("checkpoint-1", checkpoint.CheckpointId);
Assert.Equal(3, checkpoint.StepNumber);
Assert.EndsWith(
Path.Combine("Aryx", "workflow-checkpoints", command.SessionId, command.RequestId),
checkpoint.StorePath);
}
[Fact]
public async Task HandleWorkflowEventAsync_EmitsExecutorFailedDiagnostic()
{