Remove the isPackaged guard from AutoUpdateService.start() so that
background update checks run in both packaged and dev builds.
forceDevUpdateConfig already handles dev-mode config correctly via
dev-app-update.yml — the start() gate was preventing the 10s initial
check and 4h periodic checks from ever being scheduled in dev.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wire message-reclassified sidecar events through the main process to
the renderer, where reclassified messages are filtered out of the main
transcript and collected into a collapsible ThinkingProcess component.
Main process changes:
- Route message-reclassified via dedicated onMessageReclassified callback
- Add applyMessageReclassified handler that sets messageKind and emits
the session event for the renderer
- Forward assistant-intent and reasoning-delta as turn-scoped events
Renderer changes:
- Split session.messages into visibleMessages and thinkingMessages
- Render ThinkingProcess above the last assistant message
- ThinkingProcess auto-expands during active turns, collapses on finish
- Update messagePhase to skip thinking-kind messages for final detection
Tests:
- 3 new sessionWorkspace tests for reclassification apply/dedup/ignore
- 2 new messagePhase tests for thinking-kind handling
- Updated runTurnPending test fixtures for new callback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add dev-app-update.yml so electron-updater can check GitHub releases
even when running with bun run dev. Remove the isPackaged guard from
checkForUpdates() so manual clicks always contact the update server.
Automatic periodic checks remain disabled in dev mode.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When electron-updater reports no update available, the service now
transitions to 'up-to-date' instead of reverting silently to 'idle'.
The troubleshooting UI shows a green check icon, 'Up to date' label,
and 'You are running the latest version of Aryx.' description.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When multiple MCP servers expose tools with the same name, the approval
pill showed an incorrect count (e.g. 150/300) even when everything was
approved. The numerator used a Set<string> to deduplicate by tool ID,
so shared tools were counted once, while the denominator counted each
group occurrence independently.
Extract countApprovedToolsInGroups() into shared/domain/tooling.ts and
switch to per-group counting that mirrors the totalItemCount formula.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Relax branchSessionRecord validation to accept user or assistant roles
- Show "Branch from here" hover button on all conversation messages
- Contextual tooltip: "starting from this message" vs "continuing from this response"
- Add test for branching from assistant message
- Replace non-user rejection test with non-conversation rejection test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the backend contract and session-domain support for
'Branch from here':
- new branchSession IPC method and sessions:branch channel
- branchOrigin metadata on SessionRecord
- session branching helper that truncates the transcript at a
chosen user message and clears runtime state
- AryxAppService branching flow with scratchpad directory support
- persistence normalization and regression coverage
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add three layers of usage visibility:
- ChatPane footer: premium request count, AIU consumed, and quota
remaining below the existing context-window bar
- Settings / CopilotStatusCard: on-demand account quota section with
progress bars, overage indicators, and reset dates fetched via
the new get-quota sidecar command
- Activity Panel: per-agent token/cost/duration totals on each agent
row and a Session Usage summary section between agents and timeline
Backend (sidecar):
- New get-quota command using SDK account.getQuota RPC
- New assistant-usage turn-scoped event from SDK assistant.usage
- QuotaSnapshotMapper for both typed and untyped SDK quota payloads
- DTOs: GetQuotaCommandDto, QuotaSnapshotDto, AccountQuotaResultEventDto,
AssistantUsageEventDto
Frontend:
- Shared types: AssistantUsageEvent, QuotaSnapshot, GetQuotaCommand
- IPC bridge: getQuota channel, assistant-usage event dispatch
- State: SessionRequestUsageMap accumulator with per-agent breakdown
- 8 new tests for accumulator logic and formatting helpers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The CodeHighlightPlugin's selection restoration could create an invalid
Lexical selection targeting a LineBreakNode with type 'element'. Since
LineBreakNode is not an ElementNode, Lexical threw during reconciliation
and the LexicalErrorBoundary replaced the editor with an error state.
The fix ensures findPoint never targets a LineBreakNode directly.
Instead it falls back to an element-level selection on the parent
CodeNode using the child index, which is always a valid target.
Extracted the selection helpers (getCodeNodeAbsoluteOffset,
findCodeNodeSelectionPoint, restoreCodeNodeSelection) into
markdownEditor.ts for testability and added regression tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Display compact status cards in the chat transcript showing each
sub-agent's name, current activity (Thinking, Using grep, etc.),
and elapsed time. Cards appear while sub-agents are running and
clear when the session goes idle.
- Extend SessionEventRecord with description, error, toolCallId,
and model fields for subagent events
- Forward additional subagent fields in handleTurnScopedEvent
- Add subagentTracker reducer to derive active subagent state from
session events, including agent-activity correlation
- Create SubagentActivityCard component with spinner, status icon,
agent name, activity label, and live elapsed timer
- Wire activeSubagents state in App.tsx and pass to ChatPane
- Add 16 unit tests for the subagent tracker
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the agent produces multiple intermediate responses during a turn,
each gets a unique messageId but all stay pending until finalizeTurn().
This caused multiple stacked 'Thinking' bubbles in the chat transcript.
Now, when a new messageId arrives in applyTurnDelta, any previously
pending assistant messages are marked complete and message-complete
events are emitted before the new message-delta. The renderer reducer
mirrors this logic for defensive consistency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- add a PTY manager with platform shell resolution and streamed data/exit events
- expose terminal lifecycle and terminal height IPC through preload and app service
- persist terminal panel height in workspace settings and document the backend contract
- add backend tests and validate native packaging with bun run package
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mergeDiscoveredToolingState now carries over probedTools from existing
servers when the config fingerprint is unchanged. The equality check in
syncProjectDiscoveredTooling also strips probedTools before comparing,
so runtime-only probe data never triggers a spurious state replacement.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Some MCP servers (e.g., eschat.microsoft.com/mcp) serve their
OAuth Protected Resource Metadata at the origin without the path
suffix. Add a third candidate URL that strips the path, matching
VS Code's discovery behavior.
Tried in order:
1. RFC 9728: {origin}/.well-known/{suffix}{path}
2. Appended: {origin}{path}/.well-known/{suffix}
3. Origin-only: {origin}/.well-known/{suffix}
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When multiple MCP servers expose the same tool names (e.g., several
kusto-mcp instances), each server's group now shows its tools instead
of only the first server getting the tools and others showing 0/0.
Deduplicate the effectiveAutoApprovedCount to avoid double-counting
shared tools in the pill header.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When MCP server configs declare wildcard tools (empty tools array),
the Copilot SDK has no API to list individual tool names. Add direct
MCP protocol probing using @modelcontextprotocol/sdk to discover
available tools from each accepted server.
- Add McpToolProber service supporting stdio/SSE/HTTP transports
- Probe accepted MCP servers on project load, acceptance, and rescan
- Store probed tools on DiscoveredMcpServer and McpServerDefinition
- Use probed tools in listApprovalToolDefinitions when declared tools
are empty, so the approval pill shows individual tool toggles
- Remove unused isMcpServerApprovalKey helper
- Fix effectiveAutoApprovedCount Math.max workaround in ChatPane
- Add comprehensive tests for probed tool behavior
- Update ARCHITECTURE.md tooling integration section
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MCP servers configured with empty tools arrays (wildcard) now appear in the
auto-approval pill with a server-level toggle. When toggled, a server-level
approval key (mcp_server:<name>) is added to autoApprovedToolNames. The
sidecar matches this key against PermissionRequestMcp.ServerName to auto-
approve all tools from that server without needing individual tool names.
- Add buildMcpServerApprovalKey/isMcpServerApprovalKey helpers
- Create approval groups for all MCP servers including empty-tools ones
- Add serverApprovalKey to ApprovalToolGroup for server-level toggles
- Update sidecar RequiresToolCallApproval to check server-level keys
- Include server-level keys in listApprovalToolNames for pruning safety
- Add tests for both shared domain and sidecar approval matching
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Group MCP tools by server and LSP tools by profile in the approval popover
instead of showing a flat list. Each server/profile group gets a collapsible
header with a batch toggle to approve/unapprove all tools at once. Add a
search filter (visible when >10 tools) and partial-state indicators for
groups with mixed approval. Add groupApprovalToolsByProvider shared helper
with tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Give each scratchpad session its own working directory, migrate existing scratchpad sessions on workspace load, and route sidecar turns through the session-specific cwd. Add regression coverage for create, duplicate, delete, and migration flows, and document the new persistence model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the old disconnected-node addAgentNodeToGraph with the
mode-aware addAgentToGraph so new agents are automatically wired
into the graph topology. Replace the graph-nuking removeAgent
path with removeAgentFromGraph for surgical node removal. Split
emitChange into graph-preserving and mode-change paths so editing
agent properties no longer discards custom positions and edges.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add mode-aware graph mutation helpers for pattern topology so backend
code can add and remove agents without forcing a full graph rebuild.
Also preserve an existing pattern graph in savePattern instead of
re-syncing it from defaults, and cover the new behavior with shared
and main-process tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use provider-aware OAuth handling instead of a single hardcoded client ID.
GitHub now follows VS Code's GitHub auth setup with the GitHub client ID,
GitHub authorize/token endpoints, prompt=select_account, vscode.dev/redirect,
and JSON-compatible token exchange. Entra keeps the Copilot client ID via a
known provider match, while unknown providers still fall back to metadata
and dynamic registration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Some MCP servers place .well-known metadata at the appended path
(e.g. /v1/.well-known/oauth-protected-resource) instead of the
RFC 9728 compliant inserted path. Now tries the RFC-compliant URL
first and falls back to the appended form.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a user enables an HTTP MCP server for a session, proactively probe
the server URL. If it returns 401 and has discoverable OAuth metadata
(RFC 9728), automatically trigger the full OAuth 2.1 + PKCE flow and
open the browser for consent — matching VS Code's behavior.
- Add requiresOAuth() probe: GET server URL → check 401 → check PRM
- Add probeAndAuthenticateHttpMcpServers() in AryxAppService
- Call proactive probe from updateSessionTooling (fire-and-forget)
- Skip servers that already have stored tokens
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create mcpTokenStore: in-memory token storage keyed by normalized server URL
with automatic expiry checking
- Create mcpOAuthService: full OAuth 2.1 + PKCE flow implementation
- Protected Resource Metadata discovery (RFC 9728)
- Authorization Server Metadata fetch (RFC 8414)
- Dynamic Client Registration (RFC 7591) when no static client ID
- PKCE S256 code challenge generation
- Local HTTP callback server for auth code receipt
- Browser-based consent via Electron shell.openExternal
- Authorization code to token exchange
- Add startSessionMcpAuth IPC channel and handler to trigger OAuth flow
- Inject stored OAuth tokens as Authorization headers in buildRunTurnToolingConfig
- Update McpAuthBanner with 'Authenticate in browser' button and loading state
- Add tests for token store (7 tests) and token injection (3 tests)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add McpOauthRequiredEvent and McpOauthStaticClientConfigEvent to sidecar contracts
- Add PendingMcpAuthRecord domain type with status tracking (pending/authenticating/failed/done)
- Add pendingMcpAuth field to SessionRecord for session-level auth state
- Wire onMcpOAuthRequired callback through sidecarProcess, runTurnPending, and AryxAppService
- Handle mcp-oauth-required events: set session pendingMcpAuth, clear on turn finalize/cancel
- Add dismissSessionMcpAuth IPC channel with preload binding and handler registration
- Create McpAuthBanner component (amber-themed, shows server name/URL, dismiss button)
- Integrate McpAuthBanner into ChatPane with placeholder text and App.tsx callback
- Update test fixtures for new RunTurnPendingCommand.onMcpOAuthRequired field
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
Add a builtin tool label registry mapping all ~30 official SDK tool IDs
to human-readable display names. Update listApprovalToolDefinitions to
resolve labels from the registry instead of showing raw tool IDs like
read_bash or fetch_copilot_cli_documentation. Expand the fallback
runtime tool catalog from 6 to 23 non-internal standard tools.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Replace zip/tar archive distribution with platform-native installers:
- Windows: NSIS installer (.exe) with per-user install to
%LOCALAPPDATA%\Programs\Aryx, Start Menu and Desktop shortcuts,
Add/Remove Programs registration, and silent install support
- macOS: DMG disk image (.dmg) with drag-to-Applications experience
via create-dmg
- Linux: Debian package (.deb) with /opt/aryx installation,
/usr/bin/aryx symlink, desktop entry, hicolor icons, and
libsecret-1-0 dependency declaration
Add scripts/create-installer.ts as the platform-dispatching entry
point. Update CI workflow to produce native installers instead of
archives. Add installerAssetName to ReleaseTarget interface.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use createRequire for node:child_process so Bun on macOS can load gitService without ESM export-shape issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve execFile from named or default child_process exports so macOS Bun can load gitService in tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generalize the scratchpad-only model/reasoning-effort selector to work
in any session whose pattern has exactly one agent. This enables model
switching above the chat input for project 1-on-1 chats, not just
scratchpads.
- Rename ScratchpadSessionConfig -> SessionModelConfig across domain,
contracts, IPC, preload, and renderer
- Replace isScratchpadProject guard with agents.length === 1 check in
EryxAppService.updateSessionModelConfig and ChatPane pills gate
- Apply session model config in buildEffectivePattern whenever present,
regardless of project type
- Seed sessionModelConfig on session creation for any single-agent
pattern
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clear only active agent statuses when a session reaches idle, and keep the terminal cancelled/error run cleanup in place. Add regression coverage for idle cleanup after cancellation-like flows.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clear only still-active side-panel agent states when a run ends as cancelled or error, while preserving completed agent statuses. Add a renderer regression test for cancelled runs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve chat tooling from the selected session's project instead of the
separately tracked selectedProjectId so project-scoped discovered MCPs do
not appear for the wrong session. Also synchronize selectedProjectId when
a session is selected and add regression tests for both behaviors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>