feat: full Copilot SDK feature parity — custom agents, hooks, image input, skills, steering, session persistence

Backend (sidecar):
- Extended ProtocolModels with DTOs for custom agents, hooks, skills,
  infinite sessions, session lifecycle, and 9 new event types
- Added CopilotManagedSessionIds for stable SDK session ID mapping
- Added CopilotSessionManager/ICopilotSessionManager for session lifecycle
- Added CopilotSessionHooks for hook registration
- Added CopilotMessageOptionsMetadata for mid-turn steering
- Extended CopilotAgentBundle to wire custom agents, hooks, skills,
  infinite sessions, and stable session IDs
- Extended CopilotTurnExecutionState to project 13 new SDK event types
- Widened ITurnWorkflowRunner callback to accept SidecarEventDto
- Added list/delete/disconnect session commands to SidecarProtocolHost
- Added AryxCopilotAgentMessageOptionsTests (14 new tests, 142 total)

Frontend (renderer + main + shared):
- Added ChatMessageAttachment type and helpers (attachment.ts)
- Extended sidecar contracts with MessageMode, 3 new command types,
  9 new event types, and agent/session config DTOs
- Extended SessionEventRecord with 6 new event kinds and ~20 fields
- Added PatternAgentCopilotConfig to pattern domain
- Added attachments support to ChatMessageRecord
- Updated sidecar client with session lifecycle methods and
  turn-scoped event routing via onTurnScopedEvent callback
- Updated main process: handleTurnScopedEvent(), deleteSession(),
  steering bypass for mid-turn messages, attachment passthrough
- Added deleteSession IPC handler and preload binding
- Added TurnEventLog state tracker with format/apply/prune helpers
- ChatPane: always-enabled composer, steering indicator, attachment
  picker with preview, image thumbnails in message history,
  context-usage bar, amber steer mode for send button
- ActivityPanel: turn events section with sub-agent, hook, skill,
  and compaction event rendering
- Sidebar: delete session action in context menu
- App.tsx: wired sessionUsage, turnEventLogs, and deleteSession

Documentation:
- AGENTS.md: added glob safety rule for node_modules
- README.md: added steering, image input, and richer observability
- ARCHITECTURE.md: added turn-scoped events, steering, and attachments
- Website: added steering and image input feature cards, updated
  live visibility and session cards

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-28 12:28:20 +01:00
co-authored by Copilot
parent 0c2973c599
commit f1fa52f9c3
40 changed files with 2515 additions and 92 deletions
+1
View File
@@ -23,6 +23,7 @@ export const ipcChannels = {
renameSession: 'sessions:rename',
setSessionPinned: 'sessions:set-pinned',
setSessionArchived: 'sessions:set-archived',
deleteSession: 'sessions:delete',
sendSessionMessage: 'sessions:send-message',
cancelSessionTurn: 'sessions:cancel-turn',
resolveSessionApproval: 'sessions:resolve-approval',
+9 -1
View File
@@ -1,5 +1,5 @@
import type { ApprovalDecision } from '@shared/domain/approval';
import type { SidecarCapabilities, InteractionMode } from '@shared/contracts/sidecar';
import type { SidecarCapabilities, InteractionMode, MessageMode } 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';
@@ -11,6 +11,7 @@ import type {
AppearanceTheme,
} from '@shared/domain/tooling';
import type { WorkspaceState } from '@shared/domain/workspace';
import type { ChatMessageAttachment } from '@shared/domain/attachment';
export interface CreateSessionInput {
projectId: string;
@@ -24,6 +25,8 @@ export interface SavePatternInput {
export interface SendSessionMessageInput {
sessionId: string;
content: string;
attachments?: ChatMessageAttachment[];
messageMode?: MessageMode;
}
export interface CancelSessionTurnInput {
@@ -125,6 +128,10 @@ export interface StartSessionMcpAuthInput {
sessionId: string;
}
export interface DeleteSessionInput {
sessionId: string;
}
export interface ElectronApi {
describeSidecarCapabilities(): Promise<SidecarCapabilities>;
refreshSidecarCapabilities(): Promise<SidecarCapabilities>;
@@ -148,6 +155,7 @@ export interface ElectronApi {
renameSession(input: RenameSessionInput): Promise<WorkspaceState>;
setSessionPinned(input: SetSessionPinnedInput): Promise<WorkspaceState>;
setSessionArchived(input: SetSessionArchivedInput): Promise<WorkspaceState>;
deleteSession(input: DeleteSessionInput): Promise<WorkspaceState>;
sendSessionMessage(input: SendSessionMessageInput): Promise<void>;
cancelSessionTurn(input: CancelSessionTurnInput): Promise<void>;
resolveSessionApproval(input: ResolveSessionApprovalInput): Promise<WorkspaceState>;
+198 -1
View File
@@ -2,6 +2,7 @@ import type { PatternDefinition, PatternValidationIssue, ReasoningEffort } from
import type { ApprovalCheckpointKind, ApprovalDecision } from '@shared/domain/approval';
import type { ChatMessageRecord } from '@shared/domain/session';
import type { RuntimeToolDefinition } from '@shared/domain/tooling';
import type { ChatMessageAttachment } from '@shared/domain/attachment';
export interface SidecarModeCapability {
available: boolean;
@@ -69,6 +70,7 @@ export interface ValidatePatternCommand {
}
export type InteractionMode = 'interactive' | 'plan';
export type MessageMode = 'enqueue' | 'immediate';
export interface RunTurnCommand {
type: 'run-turn';
@@ -77,8 +79,10 @@ export interface RunTurnCommand {
projectPath: string;
workspaceKind?: 'project' | 'scratchpad';
mode?: InteractionMode;
messageMode?: MessageMode;
pattern: PatternDefinition;
messages: ChatMessageRecord[];
attachments?: ChatMessageAttachment[];
tooling?: RunTurnToolingConfig;
}
@@ -104,13 +108,42 @@ export interface ResolveUserInputCommand {
wasFreeform: boolean;
}
export interface ListSessionsCommand {
type: 'list-sessions';
requestId: string;
filter?: CopilotSessionListFilter;
}
export interface DeleteSessionCommand {
type: 'delete-session';
requestId: string;
sessionId?: string;
copilotSessionId?: string;
}
export interface DisconnectSessionCommand {
type: 'disconnect-session';
requestId: string;
sessionId: string;
}
export interface CopilotSessionListFilter {
cwd?: string;
gitRoot?: string;
repository?: string;
branch?: string;
}
export type SidecarCommand =
| DescribeCapabilitiesCommand
| ValidatePatternCommand
| RunTurnCommand
| CancelTurnCommand
| ResolveApprovalCommand
| ResolveUserInputCommand;
| ResolveUserInputCommand
| ListSessionsCommand
| DeleteSessionCommand
| DisconnectSessionCommand;
export interface RunTurnLocalMcpServerConfig {
id: string;
@@ -150,6 +183,30 @@ export interface RunTurnToolingConfig {
lspProfiles: RunTurnLspProfileConfig[];
}
export interface RunTurnCustomAgentConfig {
name: string;
displayName?: string;
description?: string;
tools?: string[];
prompt: string;
mcpServers?: RunTurnMcpServerConfig[];
infer?: boolean;
}
export interface RunTurnInfiniteSessionsConfig {
enabled?: boolean;
backgroundCompactionThreshold?: number;
bufferExhaustionThreshold?: number;
}
export interface PatternAgentCopilotConfig {
customAgents?: RunTurnCustomAgentConfig[];
agent?: string;
skillDirectories?: string[];
disabledSkills?: string[];
infiniteSessions?: RunTurnInfiniteSessionsConfig;
}
export interface CapabilitiesEvent {
type: 'capabilities';
requestId: string;
@@ -194,6 +251,137 @@ export interface AgentActivityEvent {
toolName?: string;
}
export type SubagentEventKind = 'started' | 'completed' | 'failed' | 'selected' | 'deselected';
export interface SubagentEvent {
type: 'subagent-event';
requestId: string;
sessionId: string;
eventKind: SubagentEventKind;
agentId?: string;
agentName?: string;
toolCallId?: string;
customAgentName?: string;
customAgentDisplayName?: string;
customAgentDescription?: string;
error?: string;
model?: string;
totalToolCalls?: number;
totalTokens?: number;
durationMs?: number;
tools?: string[];
}
export interface SkillInvokedEvent {
type: 'skill-invoked';
requestId: string;
sessionId: string;
agentId?: string;
agentName?: string;
skillName: string;
path: string;
content: string;
allowedTools?: string[];
pluginName?: string;
pluginVersion?: string;
description?: string;
}
export interface HookLifecycleEvent {
type: 'hook-lifecycle';
requestId: string;
sessionId: string;
agentId?: string;
agentName?: string;
hookInvocationId: string;
hookType: string;
phase: 'start' | 'end';
success?: boolean;
input?: unknown;
output?: unknown;
error?: string;
}
export interface SessionUsageEvent {
type: 'session-usage';
requestId: string;
sessionId: string;
agentId?: string;
agentName?: string;
tokenLimit: number;
currentTokens: number;
messagesLength: number;
systemTokens?: number;
conversationTokens?: number;
toolDefinitionsTokens?: number;
isInitial?: boolean;
}
export interface SessionCompactionEvent {
type: 'session-compaction';
requestId: string;
sessionId: string;
agentId?: string;
agentName?: string;
phase: 'start' | 'complete';
success?: boolean;
error?: string;
systemTokens?: number;
conversationTokens?: number;
toolDefinitionsTokens?: number;
preCompactionTokens?: number;
postCompactionTokens?: number;
preCompactionMessagesLength?: number;
messagesRemoved?: number;
tokensRemoved?: number;
summaryContent?: string;
checkpointNumber?: number;
checkpointPath?: string;
}
export interface PendingMessagesModifiedEvent {
type: 'pending-messages-modified';
requestId: string;
sessionId: string;
agentId?: string;
agentName?: string;
}
export interface CopilotSessionInfo {
copilotSessionId: string;
managedByAryx: boolean;
sessionId?: string;
agentId?: string;
startTime: string;
modifiedTime: string;
summary?: string;
isRemote: boolean;
cwd?: string;
gitRoot?: string;
repository?: string;
branch?: string;
}
export interface SessionsListedEvent {
type: 'sessions-listed';
requestId: string;
sessions: CopilotSessionInfo[];
}
export interface SessionsDeletedEvent {
type: 'sessions-deleted';
requestId: string;
sessionId?: string;
sessions: CopilotSessionInfo[];
}
export interface SessionDisconnectedEvent {
type: 'session-disconnected';
requestId: string;
sessionId: string;
cancelledRequestIds: string[];
}
export interface PermissionDetail {
kind: string;
intention?: string;
@@ -292,9 +480,18 @@ export type SidecarEvent =
| TurnDeltaEvent
| TurnCompleteEvent
| AgentActivityEvent
| SubagentEvent
| SkillInvokedEvent
| HookLifecycleEvent
| SessionUsageEvent
| SessionCompactionEvent
| PendingMessagesModifiedEvent
| ApprovalRequestedEvent
| UserInputRequestedEvent
| McpOauthRequiredEvent
| ExitPlanModeRequestedEvent
| SessionsListedEvent
| SessionsDeletedEvent
| SessionDisconnectedEvent
| CommandErrorEvent
| CommandCompleteEvent;