feat: add ask_user support to sidecar

Implement the Copilot SDK user input round-trip in the backend:
- add user-input-requested and resolve-user-input protocol DTOs
- add a CopilotUserInputCoordinator that mirrors approval flow with
  pending TaskCompletionSource state and explicit resolution
- wire SessionConfig.OnUserInputRequest through CopilotAgentBundle
  and CopilotWorkflowRunner
- extend SidecarProtocolHost to emit user input events, accept
  resolve-user-input commands, and filter ask_user from runtime
  approval tools
- add regression tests for the new coordinator and protocol flow

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-26 17:34:49 +01:00
co-authored by Copilot
parent 4c01d8b1a7
commit 2ae4bd01b4
8 changed files with 482 additions and 8 deletions
@@ -8,6 +8,7 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
{
private readonly PatternValidator _patternValidator;
private readonly CopilotApprovalCoordinator _approvalCoordinator = new();
private readonly CopilotUserInputCoordinator _userInputCoordinator = new();
public CopilotWorkflowRunner(PatternValidator patternValidator)
{
@@ -19,6 +20,7 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
Func<TurnDeltaEventDto, Task> onDelta,
Func<AgentActivityEventDto, Task> onActivity,
Func<ApprovalRequestedEventDto, Task> onApproval,
Func<UserInputRequestedEventDto, Task> onUserInput,
CancellationToken cancellationToken)
{
PatternValidationIssueDto? validationError = _patternValidator.Validate(command.Pattern).FirstOrDefault();
@@ -38,6 +40,13 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
state.ToolNamesByCallId,
onApproval,
cancellationToken),
(agent, request, invocation) => _userInputCoordinator.RequestUserInputAsync(
command,
agent,
request,
invocation,
onUserInput,
cancellationToken),
(agent, sessionEvent) => state.ObserveSessionEvent(agent, sessionEvent),
cancellationToken);
Workflow workflow = bundle.BuildWorkflow(command.Pattern);
@@ -66,6 +75,13 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
return _approvalCoordinator.ResolveApprovalAsync(command, cancellationToken);
}
public Task ResolveUserInputAsync(
ResolveUserInputCommandDto command,
CancellationToken cancellationToken)
{
return _userInputCoordinator.ResolveUserInputAsync(command, cancellationToken);
}
private static async Task<bool> HandleWorkflowEventAsync(
RunTurnCommandDto command,
WorkflowEvent evt,