Skip to content

feat: Virtuosos — multi-account multiplexing and capacity-aware routing#391

Open
openasocket wants to merge 60 commits intoRunMaestro:mainfrom
openasocket:feat/virtuosos
Open

feat: Virtuosos — multi-account multiplexing and capacity-aware routing#391
openasocket wants to merge 60 commits intoRunMaestro:mainfrom
openasocket:feat/virtuosos

Conversation

@openasocket
Copy link
Contributor

@openasocket openasocket commented Feb 16, 2026

Summary

Virtuosos is a multi-account multiplexing and capacity-aware routing system for Maestro. This PR implements the full stack — from account registry and usage tracking through provider switching, health monitoring, and detailed analytics.

Core Features

  • Multi-Provider Account Management: Register and manage accounts across all supported providers — Claude Code, OpenAI Codex, Gemini CLI, OpenCode, and Factory Droid. Accounts are grouped by provider in the Accounts tab with section headers and count badges.
  • Provider Auto-Discovery: "Discover Existing" scans for installed provider config directories across all providers:
    • Claude Code: `~/.claude-*` prefix directories
    • OpenAI Codex: `~/.codex` (auth via `auth.json`)
    • Gemini CLI: `~/.gemini` (auth via `oauth_creds.json`, identity from `google_accounts.json`)
    • OpenCode: `~/.opencode`
  • Account Multiplexing: Capacity-aware routing distributes work across accounts based on real-time usage and throttle detection
  • Provider Switching (Merge-Back): One-click "Switch Provider" action migrates an agent to a different provider while preserving full conversation context and provenance chain. Archived agents show visual treatment (reduced opacity, hollow status, migration label)
  • Unarchive with Conflict Detection: Right-click context menu option to restore archived agents. Detects conflicts when another active agent shares the same name and provider — prompts user to archive or delete the conflicting agent first
  • Provider Health Dashboard: Live error monitoring with health cards, error rate tracking, and automated failover configuration
  • Provider Detail View: Click into any provider for deep analytics:
    • Summary stats (total queries, avg duration, reliability %, error rate)
    • Comparison benchmarks against fleet averages
    • 5 visualization charts (daily trend, hourly activity, duration distribution, error timeline, response time heatmap)
    • Clickable session list with migration timeline
    • Inline error breakdown bar
  • Usage Analytics: Per-token-type breakdown, sparklines, trend indicators, P90 predictions, and time-range selectors (24h/7d/30d/monthly)
  • Automated Failover: ProviderErrorTracker monitors error patterns and can automatically switch agents to healthy providers when thresholds are exceeded

Technical Highlights

  • `MultiplexableAgent` type extended to `'claude-code' | 'codex' | 'opencode' | 'factory-droid' | 'gemini-cli'`
  • Multi-provider discovery via `ProviderDiscoveryConfig` pattern — each provider has its own dir pattern, auth files, and identity extractor
  • Server-side SQL aggregation for per-agent hourly data (`byAgentByHour`), matching the proven `byAgentByDay` pattern
  • Encore Feature gating — entire Virtuosos feature set gated behind `encoreFeatures.virtuosos` toggle
  • SSH-aware spawning — all agent spawn paths support SSH remote execution
  • Stats DB v4 migration — account usage tracking integrated into existing stats infrastructure

Recent Fixes

  • Multi-provider discovery: `discoverExistingAccounts()` rewritten from Claude-only (`~/.claude-*`) to multi-provider scanner detecting Codex, Gemini CLI, OpenCode installs
  • Provider-grouped accounts UI: Accounts tab groups accounts by provider type with section headers instead of a flat list
  • Create account with provider selector: New account creation form includes provider dropdown
  • Unarchive conflict detection: Requires both `name` AND `toolType` match — prevents unrelated agents from blocking unarchive
  • Modal accessibility: Fixed archive button contrast (white on teal → `accentForeground` on accent)
  • Hourly chart data: Server-side SQL aggregation replaces broken client-side computation
  • Duplicate reliability display: Removed redundant gauge chart, added compact inline error breakdown

Test Plan

  • Account CRUD (create, edit, delete, list)
  • Multi-provider discovery finds Claude, Codex, and Gemini CLI installs
  • Accounts grouped by provider in the Accounts tab
  • Provider selector in Create New Virtuoso form
  • Capacity-aware account selection routes to least-used account
  • Provider switch creates new session with transferred context
  • Source session archived with visual treatment after switch
  • Unarchive detects conflicts by name + provider type
  • Unarchive modal offers archive/delete options for conflicting agent
  • Provider Health Dashboard shows live error rates
  • Provider Detail View charts render with real data
  • Hourly activity chart populated via server-side aggregation
  • Feature gated behind Encore Features toggle
  • TypeScript compiles cleanly, all tests pass (485 files, 20235 tests)

Closes #408

🤖 Generated with Claude Code

openasocket and others added 30 commits February 15, 2026 01:41
Add foundational type definitions for the account multiplexing system:
- AccountProfile, AccountUsageSnapshot, AccountAssignment for core data models
- AccountSwitchConfig and AccountSwitchEvent for auto-switching behavior
- AccountCapacityMetrics for capacity planning
- AccountId, AccountStatus, AccountAuthMethod, MultiplexableAgent type aliases
- ACCOUNT_SWITCH_DEFAULTS and DEFAULT_TOKEN_WINDOW_MS constants
- accountId and accountName optional fields on Session interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the persistence layer for account multiplexing:
- AccountStoreData schema type for electron-store
- maestro-accounts store instance in initializeStores()
- AccountRegistry class with full CRUD, assignment management,
  round-robin/least-used selection, and switch config
- 37-test suite for AccountRegistry covering all operations
- Updated instances.test.ts store count assertion (8→9)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion)

- Bump STATS_DB_VERSION from 3 to 4
- Add migration v4: account_id + token/cost columns to query_events,
  account_usage_windows table, account_throttle_events table
- Extend QueryEvent interface with accountId, token counts, and costUsd
- Update insertQueryEvent INSERT to include new columns
- Add account-usage.ts module with upsertAccountUsageWindow,
  getAccountUsageInWindow, insertThrottleEvent, getThrottleEvents
- Wire new module into StatsDB class following delegated pattern
- Update row-mappers for new QueryEventRow fields
- Fix test expectations for version 4 and new INSERT parameters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Registers 16 IPC handlers for the accounts: namespace covering CRUD,
assignments, usage queries, throttle events, switch config, and account
selection. Extends the preload accounts API with corresponding invoke
methods alongside the existing event listeners from ACCT-MUX-04. Wires
the account usage listener into the process listener module for
real-time usage tracking and limit notifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation

Adds account-setup.ts with directory creation, symlink management,
account discovery, email extraction, and SSH remote validation.
Registers 9 IPC handlers and preload bridge methods for the full
account setup lifecycle. Includes 25 unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds Accounts tab to Settings modal with:
- Account list with status badges, default indicators, and inline config
- Discover existing accounts and import them
- Create new account flow with login command generation
- Per-account token limit, window duration, and auto-switch toggle
- Symlink validation and repair buttons
- Global switch configuration (thresholds, strategy, prompt toggle)
- CLAUDE_CONFIG_DIR conflict detection banner for manual overrides
- MaestroAPI type declaration for window.maestro.accounts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix TypeScript errors in AccountUsageDashboard (TS2783 duplicate property
overwrites), add 'accounts' to DashboardSkeleton viewMode type, remove
unused lucide imports, and update UsageDashboardModal tests for the new
Accounts tab (tab count 4→5, wrap-around nav, accounts IPC mocks).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add AccountThrottleHandler that bridges rate-limit error detection with
account switching. When rate_limited or auth_expired errors are detected
on sessions with account assignments, records the throttle event in stats
DB, marks the account as throttled, and notifies the renderer with switch
recommendations (prompt or auto-switch depending on config).

Also adds auto-recovery from throttle state when the usage window advances,
and preload bridge events for throttle/switch/status-changed notifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd resume

Implements the core account switch mechanism (ACCT-MUX-10):
- AccountSwitcher service: kill process → reassign account → respawn with new CLAUDE_CONFIG_DIR → re-send last prompt
- Prompt recording in process:write handler for seamless resume after switch
- IPC handlers for execute-switch and cleanup-session
- Renderer respawn handler with toast notifications for switch lifecycle events
- Preload bridge with typed events (switch-started, switch-respawn, switch-completed, switch-failed)
- Session cleanup on agent deletion (removes account assignment and prompt tracking)
- Type declarations in global.d.ts for all new accounts API methods
- Comprehensive test suite for AccountSwitcher (7 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nt selector

Implements the UI layer for account multiplexing switches:
- AccountSwitchModal: confirmation modal for prompted switches (throttle/limit events)
  with reason-based headers, account status display, and action buttons
- AccountSelector: compact dropdown in InputArea footer for manual account switching
  per session, with status dots, usage bars, and Manage Accounts link
- App.tsx: onSwitchPrompt listener shows modal, onSwitchCompleted toast notification
- Added ACCOUNT_SWITCH priority (1005) to modalPriorities.ts
- Added 'accounts' to SettingsTab type for View All Accounts navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire CLAUDE_CONFIG_DIR injection into every code path that spawns Claude Code
agents: standard process:spawn, Group Chat participants/moderators/synthesis,
Context Grooming, and session resume. Add missing onAssigned type declaration
to global.d.ts to fix lint error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…y for account multiplexing

Ensures account assignments survive app restarts by reconciling accounts on
session restore. Adds typed accountId to spawn config, reconcileAssignments()
to AccountRegistry, and an accounts:reconcile-sessions IPC handler called
after session restore to validate/correct account state and CLAUDE_CONFIG_DIR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds CLI-side account reading, --account and --account-rotation flags
for the playbook command, CLAUDE_CONFIG_DIR injection in agent-spawner,
round-robin account rotation in batch-processor, and a new 'accounts'
command to list configured accounts from the terminal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ations

Wire up account multiplexing support for the MergeSessionModal and all
merge-related operations:

- Display source session account (green dot indicator) in token preview
- Show target session account in tab list items during selection
- Warn when merging across different accounts (info note about symlinks)
- Propagate accountId/accountName to new sessions created by merge
- Thread accountId through the full context grooming chain:
  renderer → preload → IPC handler → context-groomer utility
- Add getAccountRegistry to ContextHandlerDependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and session UI surfaces

- ProcessMonitor: account badge on process list items and account info card in detail view
- SymphonyModal: account badge next to session name link on contribution cards
- SessionList: account info in tooltip content and non-clickable account entry in context menu
- All account UI gracefully hidden when no account is assigned

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests cover: timer-based recovery of throttled accounts past their window,
IPC event broadcasting (status-changed + recovery-available), multi-account
recovery with correct counts, edge cases (zero lastThrottledAt, missing
tokenWindowMs), and start/stop lifecycle including idempotency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add `selectByRemainingCapacity()` method that routes to the account
  with the most remaining token capacity in its current usage window
- Extract shared `getWindowBounds()` utility to `account-utils.ts`
  (was duplicated in throttle handler, usage listener, and IPC handlers)
- Accept optional `AccountUsageStatsProvider` in `selectNextAccount()`
  for capacity-aware routing; falls back to LRU when unavailable
- Apply 50% capacity penalty to recently-throttled accounts (within 2
  windows) to prevent ping-ponging
- Accounts without configured limits are deprioritized behind accounts
  with known remaining capacity
- Round-robin strategy remains deterministic and ignores usage stats
- Add 7 new tests covering capacity-aware selection scenarios

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ty-aware routing

All call sites that invoke selectNextAccount() now pass the optional statsDB
parameter, enabling capacity-aware account selection when the stats database
is available. This allows the least-used strategy to route to accounts with
the most remaining token capacity rather than just LRU ordering.

Callers updated:
- account-env-injector: accepts optional getStatsDB function, passes to selectNextAccount
- account-throttle-handler: passes its existing getStatsDB to selectNextAccount
- accounts IPC handler (accounts:select-next): passes getStatsDB singleton
- process handler (process:spawn): passes getStatsDB via injectAccountEnv

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… Auto Run recovery indicator

- Subscribe to account:throttled events with noAlternatives flag to pause
  Auto Runs with a specific rate_limited error when all accounts exhausted
- Add accounts:check-recovery IPC handler for manual recovery poll trigger
- Wire AccountRecoveryPoller into registerAccountHandlers dependencies
- Add recovery-specific UI in AutoRun.tsx: pulsing accent indicator with
  "Waiting for account recovery — will auto-resume" and "Check Now" button
- Subscribe to account:recovery-available for auto-resume of paused runs
- Add 6 tests for accounts:check-recovery handler (all passing)
- All 19,490 tests pass, lint clean

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s and promote to own modal

Rename all user-facing "Accounts" terminology to "Virtuosos" with "AI Account Providers"
subtitle. Move Virtuosos panel from Settings tab to its own top-level modal accessible from
the hamburger menu. Internal code (variable names, IPC channels, interfaces) unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lector, and SessionItem badge

Create shared useAccountUsage hook with real-time IPC subscription, derived
metrics (burn rate, time-to-limit), and adaptive countdown (30s default,
5s when any account is within 5 minutes of window reset).

- AccountsPanel: usage bar, token count, cost, queries, reset timer, burn rate per account
- AccountSelector: fix hardcoded 0% usage bar with live data and compact stats
- SessionItem: enhance badge tooltip to show usage percentage
- Add accounts mock to test setup for complete IPC coverage
- 17 new tests covering hook, formatTimeRemaining, and formatTokenCount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rends, and plan presets

Implements ACCT-MUX-20: per-account daily/monthly aggregation queries,
IPC handlers for historical usage, P90-weighted predictive analysis with
exponential decay, plan preset picker (Pro/Max5/Max20), collapsible
historical usage view with 7d/30d/monthly modes, throttle frequency
tracking, and window history caching for predictions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AccountSwitcher was fully implemented but never instantiated in index.ts,
leaving manual account switches from the renderer completely broken. This
adds the instantiation with correct dependencies and passes the getter to
both registerAccountHandlers and registerProcessHandlers, enabling the
accounts:execute-switch IPC and process:write recordLastPrompt paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ounds

- Add AccountAuthRecovery class: automatic OAuth re-authentication
  when agents encounter expired tokens (kill → login → respawn)
- Add 21 unit tests covering happy path, timeout, credential sync
  fallback, concurrent recovery guard, spawn errors, and edge cases
- Wire auth recovery into error-listener for auth_expired errors
- Consolidate getWindowBounds() duplicate: remove local copy from
  account-usage-listener.ts, import from account-utils.ts
- Update error-listener, process-listeners/index, types for auth
  recovery dependency injection
- Add syncCredentialsFromBase fallback in account-setup.ts
- Update renderer components and hooks for auth recovery state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace blocking alert() calls with toast notifications in AccountsPanel,
add console.warn to 5 silent catch blocks for Sentry breadcrumb visibility,
replace all hardcoded color hex values with theme.colors.error/warning,
tighten 7 loosely-typed Record<string, unknown> event handlers in global.d.ts
with specific payload interfaces, and expand test coverage for AccountSelector
(8 tests) and AccountSwitchModal (9 tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert VirtuososModal from single-panel to two-tab layout:
- Configuration tab: existing AccountsPanel (unchanged)
- Usage tab: new VirtuosoUsageView with aggregate summary, per-account
  usage cards, predictions section, historical expandables, and throttle
  event timeline
- Keyboard navigation (Cmd/Ctrl+Shift+[/]) following UsageDashboardModal
  pattern
- Dynamic modal width (720px config, 900px usage)
- Sessions prop passed from App.tsx for session assignment display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The usage view was collapsing all token types into a single totalTokens
value. The backend already stored inputTokens, outputTokens,
cacheReadTokens, and cacheCreationTokens separately but the renderer
never extracted or displayed them. Additionally, real-time usage-update
events were only broadcast for accounts with token limits configured,
leaving unlimited accounts with stale data.

Key fixes:
- Broadcast account:usage-update for ALL accounts, not just those with limits
- Add token breakdown fields to AccountUsageUpdate interface and IPC event
- Propagate inputTokens/outputTokens/cacheReadTokens/cacheCreationTokens
  through useAccountUsage hook and AccountUsageMetrics interface
- Add per-account token breakdown grid (In/Out/Cache R/Cache W) in usage cards
- Add stacked color-coded bars with legend in AccountUsageHistory
- Fix daily/monthly SQL to query account_usage_windows instead of
  query_events (which lacked account_id population)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e metrics to Virtuosos usage view

- Add RateMetrics interface and calculateRateMetrics() to useAccountUsage hook
  with tokens/hr, tokens/day, tokens/week, period-over-period deltas, and
  linear regression trend detection
- Create AccountTrendChart SVG component with full chart and compact sparkline modes
- Create AccountRateMetrics panel component with rate display and trend indicator
- Integrate 7-day sparklines into per-account usage cards
- Add burn rate trend arrows (up/down unicode indicators)
- Add new Trends & Analytics section between Predictions and Historical Usage
- Increase window history fetch from 40 to 68 for 2-week delta calculations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…onent

Removes the unnecessary default React import that caused TS6133 since
the project uses the modern JSX transform.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ccountUsageDashboard

Integrates the useAccountUsage hook into the main Usage Dashboard's Accounts tab
to surface burn rate, TTL, prediction confidence, 7-day sparklines, and daily trend
arrows alongside existing overview cards and bar charts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add migration tracking fields (migratedFromSessionId, migratedToSessionId,
migratedAt, archivedByMigration, migrationGeneration) to the Session
interface for Virtuosos provider switching provenance chains.

Add ProviderSwitchConfig interface and DEFAULT_PROVIDER_SWITCH_CONFIG
to shared/account-types.ts for automated provider failover configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/shared/account-types.ts (1)

132-132: Move import statement to the top of the file.

The import of ToolType is placed after export statements. Standard convention is to place all imports at the top of the file for readability and consistency.

Proposed fix

Move line 132 to the top of the file:

 /**
  * Account multiplexing types for managing multiple Claude Code accounts.
  * Supports usage monitoring, limit tracking, and automatic account switching.
  */
+
+import type { ToolType } from './types';

 /** Unique identifier for an account (generated UUID) */
 export type AccountId = string;

And remove from line 132:

 export const DEFAULT_TOKEN_WINDOW_MS = 5 * 60 * 60 * 1000;

-import type { ToolType } from './types';
-
 /**
  * Configuration for automated provider failover (Virtuosos vertical swapping).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shared/account-types.ts` at line 132, Move the import of ToolType so all
imports are grouped at the top of the file: locate the existing import statement
"import type { ToolType }" (currently after export statements) and cut it to
place alongside the other imports at the top of src/shared/account-types.ts,
ensuring no duplicate imports remain and preserving the "type" import modifier
and any ESLint/max-import-order rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/shared/account-types.ts`:
- Line 132: Move the import of ToolType so all imports are grouped at the top of
the file: locate the existing import statement "import type { ToolType }"
(currently after export statements) and cut it to place alongside the other
imports at the top of src/shared/account-types.ts, ensuring no duplicate imports
remain and preserving the "type" import modifier and any ESLint/max-import-order
rules.

…r identity carry-over

Extend CreateMergedSessionOptions with identity (nudgeMessage, bookmarked,
sessionSshRemoteConfig, autoRunFolderPath) and provenance (migratedFromSessionId,
migratedAt, migrationGeneration) fields. Apply them in createMergedSession so
sessions created via provider switching are born complete.

Create useProviderSwitch orchestrator hook that reuses the existing context
transfer pipeline (extractTabContext, contextGroomingService, createMergedSession)
but differs from useSendToAgent: preserves original session name, carries full
identity, pre-loads context in tab logs, and sets provenance fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/hooks/agent/useProviderSwitch.ts`:
- Around line 195-205: The try/catch around
window.maestro.agents.get(targetProvider) in useProviderSwitch.ts currently
swallows all errors; update the catch to distinguish expected "agent not
available" logic from unexpected IPC/infrastructure failures by reporting
unexpected errors to Sentry (or the app's error-tracking) and rethrowing (or
returning a failed state) so they don't silently continue; keep the existing
availability check using agentStatus and getAgentDisplayName(targetProvider) but
if agentCheckError looks like an infrastructure/IPC error, call the app's error
reporter (e.g., captureException) with agentCheckError and then throw the error
to fail fast.
- Around line 39-50: The ProviderSwitchRequest type includes archiveSource but
the switchProvider implementation in useProviderSwitch never uses it; either
remove archiveSource from the ProviderSwitchRequest interface or implement
archiving logic inside switchProvider (or the useProviderSwitch hook) that, when
archiveSource is true, calls the existing session-archiving routine (e.g.,
invoke the archiveSession/archiveCurrentTab function or dispatch the archive
action used elsewhere) after switching providers; update any callers or tests
accordingly to reflect the chosen approach and keep ProviderSwitchRequest and
switchProvider signatures consistent.

Comment on lines 39 to 50
export interface ProviderSwitchRequest {
/** Source session to switch from */
sourceSession: Session;
/** Tab ID within source session (active tab) */
sourceTabId: string;
/** Target provider to switch to */
targetProvider: ToolType;
/** Whether to groom context for target provider */
groomContext: boolean;
/** Whether to auto-archive source session after switch */
archiveSource: boolean;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n 'archiveSource' src/renderer/hooks/agent/useProviderSwitch.ts

Repository: RunMaestro/Maestro

Length of output: 119


🏁 Script executed:

sed -n '90,110p' src/renderer/hooks/agent/useProviderSwitch.ts

Repository: RunMaestro/Maestro

Length of output: 772


🏁 Script executed:

sed -n '140,200p' src/renderer/hooks/agent/useProviderSwitch.ts

Repository: RunMaestro/Maestro

Length of output: 1751


🏁 Script executed:

rg -n 'archive' src/renderer/hooks/agent/useProviderSwitch.ts

Repository: RunMaestro/Maestro

Length of output: 236


Unused parameter: archiveSource is defined but never used.

The archiveSource field is defined in ProviderSwitchRequest but is not destructured or used anywhere in the switchProvider function implementation. Either implement the archive logic within this hook, or remove the field if archiving is handled by the caller.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useProviderSwitch.ts` around lines 39 - 50, The
ProviderSwitchRequest type includes archiveSource but the switchProvider
implementation in useProviderSwitch never uses it; either remove archiveSource
from the ProviderSwitchRequest interface or implement archiving logic inside
switchProvider (or the useProviderSwitch hook) that, when archiveSource is true,
calls the existing session-archiving routine (e.g., invoke the
archiveSession/archiveCurrentTab function or dispatch the archive action used
elsewhere) after switching providers; update any callers or tests accordingly to
reflect the chosen approach and keep ProviderSwitchRequest and switchProvider
signatures consistent.

Comment on lines 195 to 205
try {
const agentStatus = await window.maestro.agents.get(targetProvider);
if (!agentStatus?.available) {
throw new Error(
`${getAgentDisplayName(targetProvider)} is not available. Please install and configure it first.`
);
}
} catch (agentCheckError) {
// If we can't check, log warning but continue
console.warn('Could not verify agent availability:', agentCheckError);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Swallowed exception may mask infrastructure failures.

The catch block logs a warning but continues execution. If window.maestro.agents.get() fails due to IPC issues or other infrastructure problems, the operation will proceed and fail later with a less informative error.

Consider either letting the error propagate (fail fast) or explicitly handling expected failure cases while reporting unexpected ones to Sentry.

🛡️ Suggested fix to report unexpected errors
 try {
 	const agentStatus = await window.maestro.agents.get(targetProvider);
 	if (!agentStatus?.available) {
 		throw new Error(
 			`${getAgentDisplayName(targetProvider)} is not available. Please install and configure it first.`
 		);
 	}
 } catch (agentCheckError) {
-	// If we can't check, log warning but continue
-	console.warn('Could not verify agent availability:', agentCheckError);
+	// Only continue if it's a timeout or network error; otherwise fail fast
+	if (agentCheckError instanceof Error && agentCheckError.message.includes('timeout')) {
+		console.warn('Agent availability check timed out, continuing:', agentCheckError);
+	} else {
+		throw agentCheckError;
+	}
 }

As per coding guidelines: "Do NOT silently swallow exceptions with try-catch-console.error blocks. Let unhandled exceptions bubble up to Sentry for error tracking."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const agentStatus = await window.maestro.agents.get(targetProvider);
if (!agentStatus?.available) {
throw new Error(
`${getAgentDisplayName(targetProvider)} is not available. Please install and configure it first.`
);
}
} catch (agentCheckError) {
// If we can't check, log warning but continue
console.warn('Could not verify agent availability:', agentCheckError);
}
try {
const agentStatus = await window.maestro.agents.get(targetProvider);
if (!agentStatus?.available) {
throw new Error(
`${getAgentDisplayName(targetProvider)} is not available. Please install and configure it first.`
);
}
} catch (agentCheckError) {
// Only continue if it's a timeout or network error; otherwise fail fast
if (agentCheckError instanceof Error && agentCheckError.message.includes('timeout')) {
console.warn('Agent availability check timed out, continuing:', agentCheckError);
} else {
throw agentCheckError;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useProviderSwitch.ts` around lines 195 - 205, The
try/catch around window.maestro.agents.get(targetProvider) in
useProviderSwitch.ts currently swallows all errors; update the catch to
distinguish expected "agent not available" logic from unexpected
IPC/infrastructure failures by reporting unexpected errors to Sentry (or the
app's error-tracking) and rethrowing (or returning a failed state) so they don't
silently continue; keep the existing availability check using agentStatus and
getAgentDisplayName(targetProvider) but if agentCheckError looks like an
infrastructure/IPC error, call the app's error reporter (e.g., captureException)
with agentCheckError and then throw the error to fail fast.

Add the confirmation modal for Virtuosos provider switching (VSWITCH-03).
Includes agent detection, radio-button provider selection with availability
badges, groom context and archive source checkboxes, token estimation,
and keyboard navigation. Uses Modal base component with layer stack
registration for Escape handling. PROVIDER_SWITCH priority placed at 1003,
near ACCOUNT_SWITCH at 1005.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/components/SwitchProviderModal.tsx`:
- Around line 239-240: The div with the onKeyDown handler (the element using
handleKeyDown) cannot receive keyboard events because it lacks focusability; add
tabIndex={0} to that div and add the "outline-none" class to preserve visual
styling (remove the eslint disable comment afterwards) so keyboard navigation
works and handleKeyDown will fire as intended.
- Around line 108-110: In SwitchProviderModal's catch block (the one that
currently does console.error('Failed to detect agents for provider switch:',
err)), replace the silent console logging with a call to our Sentry reporting
utility (e.g., reportError or captureException) and pass the caught error along
with contextual metadata (provider IDs, selected provider, user action such as
"switch provider", and any relevant state from detectAgentsForProvider or
detectAgents call) so errors are recorded in Sentry with context for debugging;
keep console.error only if you want local logs but ensure the Sentry call is the
primary error reporting mechanism.

Comment on lines 108 to 110
} catch (err) {
console.error('Failed to detect agents for provider switch:', err);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Silent exception swallowing violates error handling guidelines.

The catch block logs to console.error but doesn't report to Sentry. Per coding guidelines, use Sentry utilities for error reporting with context rather than silently swallowing exceptions.

🛡️ Proposed fix to report error to Sentry
+import { captureException } from '../utils/sentry';
 			} catch (err) {
-				console.error('Failed to detect agents for provider switch:', err);
+				captureException(err, {
+					tags: { component: 'SwitchProviderModal' },
+					extra: { sourceToolType: sourceSession.toolType },
+				});
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/SwitchProviderModal.tsx` around lines 108 - 110, In
SwitchProviderModal's catch block (the one that currently does
console.error('Failed to detect agents for provider switch:', err)), replace the
silent console logging with a call to our Sentry reporting utility (e.g.,
reportError or captureException) and pass the caught error along with contextual
metadata (provider IDs, selected provider, user action such as "switch
provider", and any relevant state from detectAgentsForProvider or detectAgents
call) so errors are recorded in Sentry with context for debugging; keep
console.error only if you want local logs but ensure the Sentry call is the
primary error reporting mechanism.

Comment on lines 239 to 240
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div className="flex flex-col gap-4" onKeyDown={handleKeyDown}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing tabIndex prevents keyboard navigation from working.

The <div> with onKeyDown handler lacks tabIndex={0}, so it cannot receive focus and keyboard events won't fire. The eslint-disable comment acknowledges this accessibility gap. Per coding guidelines, add tabIndex={0} and outline-none class to ensure focus works correctly.

⌨️ Proposed fix to enable keyboard focus
-			{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
-			<div className="flex flex-col gap-4" onKeyDown={handleKeyDown}>
+			<div
+				className="flex flex-col gap-4 outline-none"
+				onKeyDown={handleKeyDown}
+				tabIndex={0}
+				ref={(el) => el?.focus()}
+			>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div className="flex flex-col gap-4" onKeyDown={handleKeyDown}>
<div
className="flex flex-col gap-4 outline-none"
onKeyDown={handleKeyDown}
tabIndex={0}
ref={(el) => el?.focus()}
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/SwitchProviderModal.tsx` around lines 239 - 240, The
div with the onKeyDown handler (the element using handleKeyDown) cannot receive
keyboard events because it lacks focusability; add tabIndex={0} to that div and
add the "outline-none" class to preserve visual styling (remove the eslint
disable comment afterwards) so keyboard navigation works and handleKeyDown will
fire as intended.

openasocket and others added 15 commits February 19, 2026 02:58
… Agent modal

- Add "Switch Provider..." menu item to SessionContextMenu (after Edit Agent)
- Thread onSwitchProvider callback through SessionList and useSessionListProps
- Add "Switch..." button to Edit Agent modal's provider section
- Thread onSwitchProviderFromEdit through AppSessionModals and AppMasterModals
- All entry points gated behind optional callbacks (hidden when Virtuosos disabled)
- Actual SwitchProviderModal wiring deferred to VSWITCH-05

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add provider switching UI integration:
- Import SwitchProviderModal and useProviderSwitch hook
- Add switchProviderSession state for tracking which session is being switched
- Create handleSwitchProvider and handleConfirmProviderSwitch callbacks
- Pass handleSwitchProvider to useSessionListProps (gated behind encoreFeatures.virtuosos)
- Pass onSwitchProviderFromEdit to AppModals for EditAgentModal integration
- Render SwitchProviderModal in JSX near AccountSwitchModal and VirtuososModal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sessions with archivedByMigration render at 40% opacity, show a grey
hollow status dot (no animation), and display "Provider switched —
archived" indicator text below the session name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er config, and migration history

- Extend VirtuososModal from 2 tabs to 3: Accounts, Providers, Usage
- Rename "Configuration" tab to "Accounts" for clarity
- Create ProviderPanel component with three sections:
  1. Provider Status Grid showing detected agents with availability and session counts
  2. Failover Configuration with toggles, thresholds, and ordered fallback list
  3. Migration History timeline of past provider switches
- Update tests for new tab structure and add Providers tab coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…PC API, and App.tsx wiring

Implements VSWITCH-08: sliding-window error tracker that monitors consecutive
agent errors per session and emits failover suggestions when the configured
threshold is reached. The renderer subscribes to these events and opens the
SwitchProviderModal (or shows a toast for auto-switch).

- ProviderErrorTracker class with configurable threshold, window, and fallback list
- Only counts recoverable, provider-level errors (rate_limited, network_error,
  agent_crashed, auth_expired) — not token_exhaustion or session_not_found
- IPC handlers (providers:get-error-stats, get-all-error-stats, clear-session-errors)
- Preload API (window.maestro.providers namespace)
- Error listener integration: feeds agent errors into tracker
- Query-complete listener: resets error count on successful response
- App.tsx: failover suggestion subscription gated by encoreFeatures.virtuosos
- Clears provider error state on successful provider switch
- 21 unit tests covering all tracker behaviors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… health cards

Replace the basic Provider Status Grid in ProviderPanel with an enriched
health dashboard showing per-provider health cards with status badges,
error stats, health bars, and auto-refresh. Add useProviderHealth hook
for live data with 10s polling and immediate failover event updates.
Add health badge indicator on the Providers tab in VirtuososModal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hain walking

Implements findArchivedPredecessor() to walk the migration provenance chain
backwards and find archived sessions matching a target provider. Adds merge-back
orchestration to useProviderSwitch that reactivates archived sessions instead of
creating new ones during round-trip provider switches. Includes 13 tests covering
chain walking, cycle detection, identity preservation, and context log appending.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lth Dashboard

Extends useProviderHealth hook with StatsTimeRange state and per-provider
usage stats aggregation. Adds totals summary bar above health cards showing
combined queries, tokens, and cost. Moves auto-refresh indicator and refresh
button to a footer row with time range dropdown (Today/Week/Month/Quarter/All).
Updates health cards to show queries, tokens, and cost metrics. Subscribes to
onStatsUpdate for immediate dashboard refresh on new query events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… useProviderDetail hook

Clicking a health card or its "Details →" link transitions to a full-width
detail view for that provider. The view shows 12 summary metrics (queries,
tokens, cost, reliability, error rate, sessions, source/location split,
avg/p95 response times), active sessions list, and migration history.

Back button and Escape key return to the card grid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…reshold, and type fixes

- Add errorsByType field to ProviderErrorStats and compute in ProviderErrorTracker
- Fix getStats return type in preload and global.d.ts to include token fields
- Replace hardcoded failover threshold with config-driven value from providerSwitchConfig
- Remove unnecessary (e as any) type casts in useProviderDetail

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ProviderDetailCharts.tsx with query volume trend, response time trend
(with p95 band), 24-hour activity heatmap, token breakdown stacked bar,
source/location donut charts, and reliability gauge with error type badges.
Integrated into ProviderDetailView below summary metrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… timeline to Provider Detail View

Adds ComparisonBar with query/cost share progress bars and response time/reliability
rankings across providers, enhances Active Sessions list with click-to-navigate
(closes modal and sets active session), and extracts MigrationTimeline as a dedicated
sub-component with ArrowRightLeft icons and generation badges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…add unarchive context menu

- Remove duplicate reliability gauge from ProviderDetailCharts (was shown both as
  MetricCard and as Chart 6); move error breakdown inline to ProviderDetailView
- Fix empty Activity by Hour chart by adding byAgentByHour SQL aggregation,
  matching the working byAgentByDay pattern instead of client-side queryEvents
- Add "Unarchive" option to agent right-click menu for archived sessions (gated
  behind Virtuosos), with conflict detection modal when another active agent of
  the same provider exists (offers archive or delete of conflicting agent)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix false-positive conflicts by requiring both name AND toolType match
when checking for existing agents during unarchive. Previously only
toolType was checked, causing unrelated agents (e.g., AGENMONITORWIDGET)
to block unarchiving a different agent on the same provider.

Fix archive button contrast on UnarchiveConflictModal — use theme.colors.bg
instead of hardcoded #ffffff for text on accent background.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend account system from Claude-only to all supported providers:

- Widen MultiplexableAgent type to include codex, opencode, factory-droid,
  and gemini-cli alongside claude-code
- Expand discoverExistingAccounts() to scan for ~/.codex, ~/.opencode,
  ~/.gemini config directories with provider-specific auth detection
- Group accounts by provider in AccountsPanel with labeled section headers
  and count badges (e.g., "Claude Code — 2 accounts")
- Add provider selector dropdown to the Create New Virtuoso form
- Pass agentType through IPC add/discover pipeline (registry, handler,
  preload, global.d.ts)
- Fix unarchive conflict detection to match on name + toolType
- Fix archive button accessibility (theme.colors.bg for contrast)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openasocket
Copy link
Contributor Author

Multi-Provider Account Support (commits acc91b5, 29e087f)

What changed

1. Multi-provider account discovery (account-setup.ts)

The discoverExistingAccounts() function was Claude-only — it scanned for ~/.claude-* directories and nothing else. This meant Codex, Gemini CLI, and other provider installs were invisible to Virtuosos.

Rewrote it using a ProviderDiscoveryConfig pattern where each provider declares:

  • Dir pattern: prefix-based (~/.claude-*) or single-dir (~/.codex, ~/.gemini)
  • Auth files: ordered list of files to check for authentication status
  • Identity extractor: provider-specific function to pull email/account identity from auth files

Currently discovers:

Provider Directory Auth detection
Claude Code ~/.claude-* .claude.json, .credentials.json
OpenAI Codex ~/.codex auth.json, config.toml
Gemini CLI ~/.gemini oauth_creds.json, google_accounts.json
OpenCode ~/.opencode config.json, auth.json

2. Provider-grouped accounts UI (AccountsPanel.tsx)

Previously rendered a flat list of accounts. Now groups them by provider type with section headers (e.g., "Claude Code — 2 accounts", "OpenAI Codex — 1 account"). Uses a local PROVIDER_DISPLAY_NAMES map that covers all MultiplexableAgent types including gemini-cli (which isn't in ToolType yet).

3. Provider selector in account creation

"Create New Virtuoso" form now includes a provider dropdown so you can register accounts for any supported provider, not just Claude Code.

4. Type system updates

  • MultiplexableAgent extended: 'claude-code' | 'codex' | 'opencode' | 'factory-droid' | 'gemini-cli'
  • agentType plumbed through IPC, preload, and global.d.ts for accounts:add and accounts:discover-existing

5. Unarchive conflict fix (App.tsx)

Was matching on toolType only — any agent on the same provider would trigger a false conflict (e.g., an unrelated monitoring widget blocking unarchive of a different Claude agent). Now requires both name AND toolType match.

6. Modal accessibility (UnarchiveConflictModal.tsx)

Archive button had white text on teal accent background — poor contrast. Changed to theme.colors.accentForeground for proper readability.

Reasoning

The whole point of Virtuosos is multi-provider orchestration. Having account management locked to Claude Code only undermined the provider switching, failover, and health monitoring features. With this change, a user with 2 Claude accounts, 1 Codex account, and 1 Gemini CLI install sees all of them in one place, grouped by provider, discoverable with a single click.

openasocket and others added 7 commits February 28, 2026 23:23
Ran npm install to ensure lock file is consistent with main's
dependencies. Removed 33 stale packages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Align frontend ThrottleEvent interfaces with backend: rename totalTokens
to tokensAtThrottle, remove phantom recoveryAction field, add missing id
and sessionId fields. Fixes broken/undefined token display and unused
Recovery column in throttle event tables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ex and auto-focus

The onKeyDown handler div lacked tabIndex, so keyboard events (arrow
keys, Enter, Space) never fired. Added tabIndex={0}, outline-none class,
and auto-focus ref so the modal receives focus on open.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… in account flows

Replace console.error calls and silently-swallowed .catch(() => {}) with
Sentry.captureException reporting with contextual metadata across all
account/provider flows: reconciliation, account switching, provider
switching, session merging, and account assignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…face

The archiveSource field was defined in ProviderSwitchRequest but never
used by the useProviderSwitch hook. Archiving is handled entirely by
App.tsx after the hook returns, using the modal callback's own request
parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fast

Separated the IPC call from the availability check in useProviderSwitch so
infrastructure failures (window.maestro.agents.get throwing) are reported to
Sentry with context metadata and re-thrown as a user-facing error, instead of
being silently swallowed by console.warn. The !agentStatus?.available check
now correctly propagates to the outer error handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When accountName is missing but accountId exists, display accountId
as fallback in the agent tooltip. Matches the existing pattern used
in the context menu (line 258).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openasocket
Copy link
Contributor Author

Review Fixes Applied

All CodeRabbit review items addressed (2 critical, 1 major, 2 minor) plus conflict resolution verified across 18 files. Branch is fully rebased on main with no conflicts.

Critical Fixes

  • ThrottleEvent field mismatch (AccountUsageDashboard.tsx, VirtuosoUsageView.tsx) — Frontend ThrottleEvent interface expected totalTokens and recoveryAction, but backend (account-usage.ts) sends tokensAtThrottle and never sends recoveryAction. This caused undefined values in the throttle table and broken conditional rendering. Aligned both frontend interfaces with the backend definition: renamed totalTokenstokensAtThrottle, added missing id and sessionId fields, removed phantom recoveryAction field. Removed the "Recovery" column from the dashboard table and the recoveryAction conditional block from VirtuosoUsageView.

  • SwitchProviderModal keyboard navigation broken (SwitchProviderModal.tsx) — The <div> with onKeyDown handler lacked tabIndex, so keyboard events never fired — arrow key navigation and Enter/Escape were completely non-functional. Added tabIndex={0}, outline-none class, and ref={(el) => el?.focus()} auto-focus. Removed the now-unnecessary eslint-disable for jsx-a11y/no-static-element-interactions. Follows the same pattern as AutoRunLightbox.tsx.

Major Fix

  • Silent error swallowing in account flows (App.tsx, AccountSelector.tsx, SwitchProviderModal.tsx, MergeSessionModal.tsx) — 15 instances of console.error and empty .catch(() => {}) across 4 files were hiding failures from Sentry. Replaced all with Sentry.captureException(error, { extra: { operation, ...context } }). Covers: account reconciliation, switch respawn, auto-switch, account assignment, provider clearing, unarchive kill, account list fetch, agent detection, and merge execution. Decision: used operation string in every Sentry payload to make it easy to filter and triage in the Sentry dashboard.

Minor Fixes

  • Unused archiveSource parameter (useProviderSwitch.ts, ProviderSwitchRequest interface) — Defined in the interface but never consumed by the hook. Archiving is handled entirely by App.tsx after the hook returns. Removed from the interface, the switchProvider() call, and JSDoc. Left archiveSource in SwitchProviderModal's onConfirmSwitch callback type and handleConfirmProviderSwitch where it is actually consumed.

  • Agent availability check failure handling (useProviderSwitch.ts) — Separated the IPC call (window.maestro.agents.get) from the availability check into distinct try/catch blocks. Infrastructure failures (IPC throws) now report to Sentry with provider/agent context and re-throw a user-facing error, rather than being silently swallowed by the catch-all. The !agentStatus?.available path now correctly propagates to the outer catch.

  • accountId fallback in SessionList tooltip (SessionList.tsx) — Tooltip only showed accountName, displaying nothing when the name was missing but an ID existed. Changed to session.accountName || session.accountId, matching the fallback pattern already used in the context menu.

Conflict Resolution (18 files)

Branch rebased cleanly onto main with no conflict markers. Verified integration of both sides across all 18 originally-conflicting files:

File Verification
package-lock.json Regenerated via npm install (removed 33 stale packages)
src/main/index.ts All 31 IPC handler registrations present, no duplicates
src/main/preload/index.ts All preload bridges merged (accounts, providers, wakatime, tabNaming, directorNotes)
src/main/ipc/handlers/index.ts All handler exports present with AccountRegistry pass-through
src/main/group-chat/group-chat-storage.ts Both sides' fields present, accountId correctly optional
src/main/stats/schema.ts + migrations.ts Migration versions sequential (v1-v4), no collisions
src/renderer/App.tsx Account management hooks/effects interleaved with extracted hooks
InputArea.tsx, SessionList.tsx, TabBar.tsx, MainPanel.tsx, QuickActionsModal.tsx Both sides' UI features integrated
useMainKeyboardHandler.ts All 55 shortcuts verified, no key collisions
useMainPanelProps.ts + session/index.ts Hook exports merged correctly
TabBar.test.tsx All 156 tests pass, no assertion changes needed

Verification

  • ✅ 485 test files pass, 20,235 tests pass (0 failures)
  • ✅ All 3 TypeScript configs clean (zero type errors)
  • ⬜ Manual testing pending: multi-account switching, provider health dashboard, throttle event display

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add “Switch Provider” (safe migrate) for existing agent sessions

2 participants