// Copyright (c) Microsoft. All rights reserved. using System.Diagnostics; using Microsoft.Agents.AI.Workflows; using Microsoft.DurableTask; using Microsoft.DurableTask.Client; namespace Microsoft.Agents.AI.DurableTask.Workflows; /// /// Represents a durable workflow run that tracks execution status and provides access to workflow events. /// [DebuggerDisplay("{WorkflowName} ({RunId})")] internal sealed class DurableWorkflowRun : IAwaitableWorkflowRun { private readonly DurableTaskClient _client; private readonly List _eventSink = []; private int _lastBookmark; /// /// Initializes a new instance of the class. /// /// The durable task client for orchestration operations. /// The unique instance ID for this orchestration run. /// The name of the workflow being executed. internal DurableWorkflowRun(DurableTaskClient client, string instanceId, string workflowName) { this._client = client; this.RunId = instanceId; this.WorkflowName = workflowName; } /// public string RunId { get; } /// /// Gets the name of the workflow being executed. /// public string WorkflowName { get; } /// /// Waits for the workflow to complete and returns the result. /// /// The expected result type. /// A cancellation token to observe. /// The result of the workflow execution. /// Thrown when the workflow failed. /// Thrown when the workflow was terminated or ended with an unexpected status. public async ValueTask WaitForCompletionAsync(CancellationToken cancellationToken = default) { OrchestrationMetadata metadata = await this._client.WaitForInstanceCompletionAsync( this.RunId, getInputsAndOutputs: true, cancellation: cancellationToken).ConfigureAwait(false); if (metadata.RuntimeStatus == OrchestrationRuntimeStatus.Completed) { return DurableStreamingWorkflowRun.ExtractResult(metadata.SerializedOutput); } if (metadata.RuntimeStatus == OrchestrationRuntimeStatus.Failed) { if (metadata.FailureDetails is not null) { // Use TaskFailedException to preserve full failure details including stack trace and inner exceptions throw new TaskFailedException( taskName: this.WorkflowName, taskId: 0, failureDetails: metadata.FailureDetails); } throw new InvalidOperationException( $"Workflow '{this.WorkflowName}' (RunId: {this.RunId}) failed without failure details."); } throw new InvalidOperationException( $"Workflow '{this.WorkflowName}' (RunId: {this.RunId}) ended with unexpected status: {metadata.RuntimeStatus}"); } /// /// Waits for the workflow to complete and returns the string result. /// /// A cancellation token to observe. /// The string result of the workflow execution. public ValueTask WaitForCompletionAsync(CancellationToken cancellationToken = default) => this.WaitForCompletionAsync(cancellationToken); /// /// Gets all events that have been collected from the workflow. /// public IEnumerable OutgoingEvents => this._eventSink; /// /// Gets the number of events collected since the last access to . /// public int NewEventCount => this._eventSink.Count - this._lastBookmark; /// /// Gets all events collected since the last access to . /// public IEnumerable NewEvents { get { if (this._lastBookmark >= this._eventSink.Count) { return []; } int currentBookmark = this._lastBookmark; this._lastBookmark = this._eventSink.Count; return this._eventSink.Skip(currentBookmark); } } }