Public Access
PR and Push Build/Test / build-and-test (push) Successful in 12m0s
Append successful offline recovery with a duplicate-warning marker and retain failed replay sources. Offer newly available microphones through confirmation notifications. Add project discovery metadata and blocking association approval, plus nonblocking workflow requests that start a separate settings-agent conversation. Include regression tests, OpenSpec changes, and verified local Release startup.
57 lines
2.1 KiB
C#
57 lines
2.1 KiB
C#
namespace MeetingAssistant.Notifications;
|
|
|
|
public sealed record AgentApprovalPrompt(string Title, string Message, TimeSpan Timeout);
|
|
|
|
public interface IAgentApprovalPromptService
|
|
{
|
|
Task<bool> ConfirmAsync(AgentApprovalPrompt prompt, CancellationToken cancellationToken);
|
|
}
|
|
|
|
public sealed class NoopAgentApprovalPromptService : IAgentApprovalPromptService
|
|
{
|
|
public Task<bool> ConfirmAsync(AgentApprovalPrompt prompt, CancellationToken cancellationToken) => Task.FromResult(false);
|
|
}
|
|
|
|
public sealed class AgentApprovalService
|
|
{
|
|
private readonly IAgentApprovalPromptService prompts;
|
|
private readonly ILogger<AgentApprovalService> logger;
|
|
private readonly TimeSpan timeout;
|
|
|
|
public AgentApprovalService(IAgentApprovalPromptService prompts, ILogger<AgentApprovalService> logger)
|
|
: this(prompts, logger, TimeSpan.FromMinutes(10)) { }
|
|
|
|
internal AgentApprovalService(IAgentApprovalPromptService prompts, ILogger<AgentApprovalService> logger, TimeSpan timeout)
|
|
{
|
|
this.prompts = prompts;
|
|
this.logger = logger;
|
|
this.timeout = timeout;
|
|
}
|
|
|
|
public async Task<bool> ConfirmAsync(string title, string message, CancellationToken cancellationToken)
|
|
{
|
|
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
deadline.CancelAfter(timeout);
|
|
try
|
|
{
|
|
var accepted = await prompts.ConfirmAsync(new AgentApprovalPrompt(title, message, timeout), deadline.Token)
|
|
.WaitAsync(deadline.Token);
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
return accepted && !deadline.IsCancellationRequested;
|
|
}
|
|
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
return false;
|
|
}
|
|
catch (Exception exception) when (exception is not OperationCanceledException)
|
|
{
|
|
logger.LogWarning(exception, "Could not obtain user approval for {Title}", title);
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
await deadline.CancelAsync();
|
|
}
|
|
}
|
|
}
|