.NET: Fix RequestInfoEvent lost when resuming workflow from checkpoint (#4955)

* Fix RequestInfoEvent lost when resuming workflow from checkpoint

* Fix streaming run double disposal in tests and lockstep republishing before Started event is emitted.

* Fix bug to remove messages after sending to avoid losing messages on send failure.

* Fix declarative test harness
This commit is contained in:
Peter Ibekwe
2026-04-01 08:38:48 -07:00
committed by GitHub
Unverified
parent 25696a72dc
commit 38de991481
12 changed files with 748 additions and 59 deletions
@@ -50,10 +50,13 @@ public sealed class InProcessExecutionEnvironment : IWorkflowExecutionEnvironmen
return runner.BeginStreamAsync(this.ExecutionMode, cancellationToken);
}
internal ValueTask<AsyncRunHandle> ResumeRunAsync(Workflow workflow, CheckpointInfo fromCheckpoint, IEnumerable<Type> knownValidInputTypes, CancellationToken cancellationToken)
internal ValueTask<AsyncRunHandle> ResumeRunAsync(Workflow workflow, CheckpointInfo fromCheckpoint, IEnumerable<Type> knownValidInputTypes, CancellationToken cancellationToken = default)
=> this.ResumeRunAsync(workflow, fromCheckpoint, knownValidInputTypes, republishPendingEvents: true, cancellationToken);
internal ValueTask<AsyncRunHandle> ResumeRunAsync(Workflow workflow, CheckpointInfo fromCheckpoint, IEnumerable<Type> knownValidInputTypes, bool republishPendingEvents, CancellationToken cancellationToken = default)
{
InProcessRunner runner = InProcessRunner.CreateTopLevelRunner(workflow, this.CheckpointManager, fromCheckpoint.SessionId, this.EnableConcurrentRuns, knownValidInputTypes);
return runner.ResumeStreamAsync(this.ExecutionMode, fromCheckpoint, cancellationToken);
return runner.ResumeStreamAsync(this.ExecutionMode, fromCheckpoint, republishPendingEvents, cancellationToken);
}
/// <inheritdoc/>
@@ -104,6 +107,32 @@ public sealed class InProcessExecutionEnvironment : IWorkflowExecutionEnvironmen
return new(runHandle);
}
/// <summary>
/// Resumes a streaming workflow run from a checkpoint with control over whether
/// pending request events are republished through the event stream.
/// </summary>
/// <param name="workflow">The workflow to resume.</param>
/// <param name="fromCheckpoint">The checkpoint to resume from.</param>
/// <param name="republishPendingEvents">
/// When <see langword="true"/>, any pending request events are republished through the event
/// stream after subscribing. When <see langword="false"/>, the caller is responsible for
/// handling pending requests (e.g., <see cref="WorkflowSession"/> already sends responses).
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
internal async ValueTask<StreamingRun> ResumeStreamingInternalAsync(
Workflow workflow,
CheckpointInfo fromCheckpoint,
bool republishPendingEvents,
CancellationToken cancellationToken = default)
{
this.VerifyCheckpointingConfigured();
AsyncRunHandle runHandle = await this.ResumeRunAsync(workflow, fromCheckpoint, [], republishPendingEvents, cancellationToken)
.ConfigureAwait(false);
return new(runHandle);
}
private async ValueTask<AsyncRunHandle> BeginRunHandlingChatProtocolAsync<TInput>(Workflow workflow,
TInput input,
string? sessionId = null,