feat: add plan mode frontend support with mode toggle and plan review UI

- Add InteractionMode type and ExitPlanModeRequestedEvent to sidecar contracts
- Add PendingPlanReviewRecord domain type and interactionMode to SessionRecord
- Wire onExitPlanMode callback through SidecarClient and RunTurnPendingCommand
- Pass session interaction mode to RunTurnCommand for sidecar consumption
- Handle exit-plan-mode-requested events in AryxAppService
- Add setSessionInteractionMode and dismissSessionPlanReview IPC methods
- Create PlanReviewBanner component with summary, markdown content, and actions
- Add plan mode toggle pill in ChatPane composer area
- Wire implement action as follow-up message (graceful degradation)
- Clear pending plan review on new turn, turn completion, and cancellation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-26 23:39:48 +01:00
co-authored by Copilot
parent 380e402512
commit 231be36e6c
14 changed files with 279 additions and 8 deletions
+2
View File
@@ -27,6 +27,8 @@ export const ipcChannels = {
cancelSessionTurn: 'sessions:cancel-turn',
resolveSessionApproval: 'sessions:resolve-approval',
resolveSessionUserInput: 'sessions:resolve-user-input',
setSessionInteractionMode: 'sessions:set-interaction-mode',
dismissSessionPlanReview: 'sessions:dismiss-plan-review',
querySessions: 'sessions:query',
updateSessionModelConfig: 'sessions:update-model-config',
selectProject: 'selection:project',
+12 -1
View File
@@ -1,5 +1,5 @@
import type { ApprovalDecision } from '@shared/domain/approval';
import type { SidecarCapabilities } from '@shared/contracts/sidecar';
import type { SidecarCapabilities, InteractionMode } from '@shared/contracts/sidecar';
import type { PatternDefinition, ReasoningEffort } from '@shared/domain/pattern';
import type { ProjectRecord } from '@shared/domain/project';
import type { QuerySessionsInput, SessionQueryResult } from '@shared/domain/sessionLibrary';
@@ -107,6 +107,15 @@ export interface UpdateSessionApprovalSettingsInput {
autoApprovedToolNames?: string[];
}
export interface SetSessionInteractionModeInput {
sessionId: string;
mode: InteractionMode;
}
export interface DismissSessionPlanReviewInput {
sessionId: string;
}
export interface ElectronApi {
describeSidecarCapabilities(): Promise<SidecarCapabilities>;
refreshSidecarCapabilities(): Promise<SidecarCapabilities>;
@@ -134,6 +143,8 @@ export interface ElectronApi {
cancelSessionTurn(input: CancelSessionTurnInput): Promise<void>;
resolveSessionApproval(input: ResolveSessionApprovalInput): Promise<WorkspaceState>;
resolveSessionUserInput(input: ResolveSessionUserInputInput): Promise<WorkspaceState>;
setSessionInteractionMode(input: SetSessionInteractionModeInput): Promise<WorkspaceState>;
dismissSessionPlanReview(input: DismissSessionPlanReviewInput): Promise<WorkspaceState>;
updateSessionModelConfig(input: UpdateSessionModelConfigInput): Promise<WorkspaceState>;
querySessions(input: QuerySessionsInput): Promise<SessionQueryResult[]>;
selectProject(projectId?: string): Promise<WorkspaceState>;
+17
View File
@@ -68,12 +68,15 @@ export interface ValidatePatternCommand {
pattern: PatternDefinition;
}
export type InteractionMode = 'interactive' | 'plan';
export interface RunTurnCommand {
type: 'run-turn';
requestId: string;
sessionId: string;
projectPath: string;
workspaceKind?: 'project' | 'scratchpad';
mode?: InteractionMode;
pattern: PatternDefinition;
messages: ChatMessageRecord[];
tooling?: RunTurnToolingConfig;
@@ -241,6 +244,19 @@ export interface UserInputRequestedEvent {
allowFreeform?: boolean;
}
export interface ExitPlanModeRequestedEvent {
type: 'exit-plan-mode-requested';
requestId: string;
sessionId: string;
exitPlanId: string;
agentId?: string;
agentName?: string;
summary: string;
planContent: string;
actions?: string[];
recommendedAction?: string;
}
export interface CommandErrorEvent {
type: 'command-error';
requestId: string;
@@ -260,5 +276,6 @@ export type SidecarEvent =
| AgentActivityEvent
| ApprovalRequestedEvent
| UserInputRequestedEvent
| ExitPlanModeRequestedEvent
| CommandErrorEvent
| CommandCompleteEvent;