mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-09 05:08:44 +02:00
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:
@@ -14,6 +14,8 @@ public sealed class SidecarProtocolHost
|
||||
private const string RunTurnCommandType = "run-turn";
|
||||
private const string CancelTurnCommandType = "cancel-turn";
|
||||
private const string ResolveApprovalCommandType = "resolve-approval";
|
||||
private const string ResolveUserInputCommandType = "resolve-user-input";
|
||||
private const string AskUserToolName = "ask_user";
|
||||
|
||||
private static readonly string[] AuthenticationErrorIndicators =
|
||||
[
|
||||
@@ -62,6 +64,7 @@ public sealed class SidecarProtocolHost
|
||||
[RunTurnCommandType] = HandleRunTurnAsync,
|
||||
[CancelTurnCommandType] = HandleCancelTurnAsync,
|
||||
[ResolveApprovalCommandType] = HandleResolveApprovalAsync,
|
||||
[ResolveUserInputCommandType] = HandleResolveUserInputAsync,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,6 +181,7 @@ public sealed class SidecarProtocolHost
|
||||
delta => WriteAsync(context.Output, delta, turnCancellation.Token),
|
||||
activity => WriteAsync(context.Output, activity, turnCancellation.Token),
|
||||
approval => WriteAsync(context.Output, approval, turnCancellation.Token),
|
||||
userInput => WriteAsync(context.Output, userInput, turnCancellation.Token),
|
||||
turnCancellation.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -231,6 +235,12 @@ public sealed class SidecarProtocolHost
|
||||
await _workflowRunner.ResolveApprovalAsync(command, context.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task HandleResolveUserInputAsync(CommandContext context)
|
||||
{
|
||||
ResolveUserInputCommandDto command = DeserializeCommand<ResolveUserInputCommandDto>(context);
|
||||
await _workflowRunner.ResolveUserInputAsync(command, context.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private TCommand DeserializeCommand<TCommand>(CommandContext context)
|
||||
where TCommand : SidecarCommandEnvelope
|
||||
{
|
||||
@@ -427,7 +437,13 @@ public sealed class SidecarProtocolHost
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ToolsListResult result = await client.Rpc.Tools.ListAsync(null!, cancellationToken).ConfigureAwait(false);
|
||||
return result.Tools
|
||||
return MapRuntimeTools(result.Tools);
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<SidecarRuntimeToolDto> MapRuntimeTools(IEnumerable<Tool> tools)
|
||||
{
|
||||
return tools
|
||||
.Where(ShouldIncludeRuntimeTool)
|
||||
.Where(tool => !string.IsNullOrWhiteSpace(tool.Name))
|
||||
.Select(tool => new SidecarRuntimeToolDto
|
||||
{
|
||||
@@ -440,6 +456,13 @@ public sealed class SidecarProtocolHost
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static bool ShouldIncludeRuntimeTool(Tool tool)
|
||||
{
|
||||
string? toolName = string.IsNullOrWhiteSpace(tool.Name) ? null : tool.Name.Trim();
|
||||
return toolName is not null
|
||||
&& !string.Equals(toolName, AskUserToolName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsReasoningEffort(string? value)
|
||||
{
|
||||
return value is "low" or "medium" or "high" or "xhigh";
|
||||
|
||||
Reference in New Issue
Block a user