feat: add ask_user interactive user input to frontend

Wire the sidecar user-input-requested protocol event through the main
process, IPC layer, and renderer UI so agents can ask the user
interactive questions with choices and freeform input.

- Add UserInputRequestedEvent and ResolveUserInputCommand to sidecar
  protocol types
- Add resolveUserInput method to SidecarClient and onUserInput callback
  to runTurn
- Create PendingUserInputRecord domain type and add pendingUserInput to
  SessionRecord
- Add handleUserInputRequested and resolveSessionUserInput to
  AryxAppService with handle management
- Register sessions:resolve-user-input IPC channel with preload bridge
- Create UserInputBanner component with choice buttons and freeform
  input
- Integrate UserInputBanner into ChatPane with header indicator

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-26 17:45:27 +01:00
co-authored by Copilot
parent 2ae4bd01b4
commit f15b1aedb1
14 changed files with 356 additions and 12 deletions
+1
View File
@@ -26,6 +26,7 @@ export const ipcChannels = {
sendSessionMessage: 'sessions:send-message',
cancelSessionTurn: 'sessions:cancel-turn',
resolveSessionApproval: 'sessions:resolve-approval',
resolveSessionUserInput: 'sessions:resolve-user-input',
querySessions: 'sessions:query',
updateSessionModelConfig: 'sessions:update-model-config',
selectProject: 'selection:project',
+8
View File
@@ -36,6 +36,13 @@ export interface ResolveSessionApprovalInput {
decision: ApprovalDecision;
}
export interface ResolveSessionUserInputInput {
sessionId: string;
userInputId: string;
answer: string;
wasFreeform: boolean;
}
export interface UpdateSessionModelConfigInput {
sessionId: string;
model: string;
@@ -126,6 +133,7 @@ export interface ElectronApi {
sendSessionMessage(input: SendSessionMessageInput): Promise<void>;
cancelSessionTurn(input: CancelSessionTurnInput): Promise<void>;
resolveSessionApproval(input: ResolveSessionApprovalInput): Promise<WorkspaceState>;
resolveSessionUserInput(input: ResolveSessionUserInputInput): Promise<WorkspaceState>;
updateSessionModelConfig(input: UpdateSessionModelConfigInput): Promise<WorkspaceState>;
querySessions(input: QuerySessionsInput): Promise<SessionQueryResult[]>;
selectProject(projectId?: string): Promise<WorkspaceState>;
+23 -1
View File
@@ -92,12 +92,21 @@ export interface ResolveApprovalCommand {
decision: ApprovalDecision;
}
export interface ResolveUserInputCommand {
type: 'resolve-user-input';
requestId: string;
userInputId: string;
answer: string;
wasFreeform: boolean;
}
export type SidecarCommand =
| DescribeCapabilitiesCommand
| ValidatePatternCommand
| RunTurnCommand
| CancelTurnCommand
| ResolveApprovalCommand;
| ResolveApprovalCommand
| ResolveUserInputCommand;
export interface RunTurnLocalMcpServerConfig {
id: string;
@@ -220,6 +229,18 @@ export interface ApprovalRequestedEvent {
permissionDetail?: PermissionDetail;
}
export interface UserInputRequestedEvent {
type: 'user-input-requested';
requestId: string;
sessionId: string;
userInputId: string;
agentId?: string;
agentName?: string;
question: string;
choices?: string[];
allowFreeform?: boolean;
}
export interface CommandErrorEvent {
type: 'command-error';
requestId: string;
@@ -238,5 +259,6 @@ export type SidecarEvent =
| TurnCompleteEvent
| AgentActivityEvent
| ApprovalRequestedEvent
| UserInputRequestedEvent
| CommandErrorEvent
| CommandCompleteEvent;