- AI_REFACTOR_PLAN.md: Electron-only, 7 phases, 18 steps - BACKEND_PLAN.md: standalone FastAPI backend guide for separate repo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
390 lines
23 KiB
Markdown
390 lines
23 KiB
Markdown
# AI Refactor Plan — Adiuva Electron App
|
|
|
|
> **Objective:** Transform the Electron app from a single-process AI integration into a local-first multi-agent client with plugin-based batch agents, multi-provider LLM support, E2E encrypted backup, granular permissions, and cloud backend integration.
|
|
>
|
|
> **Backend:** Lives in a separate repository. See `BACKEND_PLAN.md` for the API contract and backend implementation guide.
|
|
>
|
|
> **Protocol:** Execute steps sequentially. Each step is atomic and committable. Mark `[x]` when done.
|
|
|
|
---
|
|
|
|
## Phase 0 — API Contracts & Types
|
|
|
|
### Step 0.1 — Define backend API contract types
|
|
- [ ] Create `src/shared/api-types.ts` with all interfaces the Electron app needs to communicate with the backend:
|
|
- `ExecutionPlan`, `PlanStep`, `PlanAction` (action types: `create_record`, `update_record`, `delete_record`, `index_document`, `send_notification`)
|
|
- `ChatRequest` (message, context, execution_mode: `'direct'` | `'plan'`)
|
|
- `ChatResponse` (response, actions)
|
|
- `ChatContext` (user_profile, relevant_documents, recent_tasks, conversation_history)
|
|
- `AgentManifest` (name, description, permissions, schedule)
|
|
- `PermissionGrant` (plugin, permission type, resource path, granted_at)
|
|
- `BackupMetadata` (version, timestamp, checksum, chunk_count)
|
|
- `BillingTier` enum (`free`, `pro`, `power`, `team`)
|
|
- `AuthTokens` (access_token, refresh_token, expires_at)
|
|
- `UserProfile` (id, email, tier)
|
|
- [ ] Update `tsconfig.json` paths if needed to include `src/shared/`
|
|
- **Files:** `src/shared/api-types.ts`, `tsconfig.json`
|
|
- **Outcome:** Type-safe contracts for all backend communication. Backend repo mirrors these as Pydantic schemas.
|
|
|
|
---
|
|
|
|
## Phase 1 — LiteLLM Multi-Provider Client
|
|
|
|
### Step 1.1 — Create unified LLM client wrapper
|
|
- [ ] Create `src/main/llm/litellm-client.ts`:
|
|
- `LiteLLMClient` class with unified interface:
|
|
- `complete(messages: Message[], options?: CompletionOptions): Promise<CompletionResponse>`
|
|
- `stream(messages: Message[], options?: CompletionOptions): AsyncGenerator<string>`
|
|
- `embed(text: string): Promise<number[]>`
|
|
- `CompletionOptions`: model override, temperature, max_tokens, tools
|
|
- Provider-agnostic: internally maps to the correct provider SDK
|
|
- Fallback chain: tries primary provider, on failure tries secondary, logs each attempt
|
|
- Timeout handling: per-provider configurable timeouts
|
|
- [ ] Create `src/main/llm/providers.ts`:
|
|
- `ProviderConfig` interface: name, apiKey, model, endpoint (for Ollama), timeout, isLocal
|
|
- `ProviderRegistry`: manages configured providers, persists to electron-store
|
|
- `getActiveProvider()`, `setActiveProvider(name)`, `addProvider(config)`, `removeProvider(name)`
|
|
- `getFallbackChain(): ProviderConfig[]`
|
|
- Supported providers: OpenAI, Anthropic, Google (Gemini), Mistral, Groq, Ollama (local)
|
|
- [ ] Create `src/main/llm/embeddings.ts` (refactored):
|
|
- Support multiple embedding providers (OpenAI text-embedding-3-small, local ONNX with all-MiniLM-L6-v2)
|
|
- Auto-select: use local ONNX if available, fall back to API
|
|
- Same `embedText(text): Promise<number[]>` interface
|
|
- **Files:** `src/main/llm/litellm-client.ts`, `src/main/llm/providers.ts`, `src/main/llm/embeddings.ts`
|
|
- **Outcome:** Single LLM interface that all local components use. Supports 6+ providers with fallback.
|
|
|
|
### Step 1.2 — Migrate existing AI code to use new LLM client
|
|
- [ ] Update `src/main/ai/orchestrator.ts`:
|
|
- Replace direct `getLLM()` calls with `LiteLLMClient.complete()` / `LiteLLMClient.stream()`
|
|
- Keep local orchestration working with the new client (backend delegation comes in Phase 3)
|
|
- [ ] Update `src/main/ai/llm.ts`:
|
|
- Deprecate. Redirect `getLLM()` to instantiate via `LiteLLMClient` as a thin compatibility shim
|
|
- [ ] Update `src/main/ai/embeddings.ts` to delegate to `src/main/llm/embeddings.ts`
|
|
- [ ] Update `src/main/ai/token.ts`:
|
|
- Add `listStoredProviders(): Promise<string[]>` to enumerate which providers have tokens
|
|
- [ ] Ensure all existing AI features (chat, daily brief, tool calling) continue to work
|
|
- **Files:** `src/main/ai/orchestrator.ts`, `src/main/ai/llm.ts`, `src/main/ai/embeddings.ts`, `src/main/ai/token.ts`
|
|
- **Outcome:** Existing AI features work identically but go through the new unified LLM client.
|
|
|
|
---
|
|
|
|
## Phase 2 — Local Plugin System & Batch Agents
|
|
|
|
### Step 2.1 — Create plugin manifest system and permission manager
|
|
- [ ] Create `src/main/permissions/manifest-validator.ts`:
|
|
- `PluginManifest` interface: `name`, `description`, `version`, `permissions: PermissionRequest[]`, `schedule?: string` (cron), `entryPoint: string`
|
|
- `PermissionRequest`: `type` (read_folder, read_email, read_calendar, read_browser_history), `resource?: string` (path, account), `reason: string`
|
|
- `validateManifest(manifest): ValidationResult` — validates structure, checks for dangerous permissions
|
|
- [ ] Create `src/main/permissions/permission-manager.ts`:
|
|
- `PermissionManager` class (singleton):
|
|
- `grantPermission(pluginName, permission): void` — persists to SQLite
|
|
- `revokePermission(pluginName, permission): void`
|
|
- `checkPermission(pluginName, permission): boolean`
|
|
- `getPluginPermissions(pluginName): PermissionGrant[]`
|
|
- `getAllGrants(): PermissionGrant[]`
|
|
- `logAccess(pluginName, permission, resource, timestamp): void` — activity log
|
|
- `getActivityLog(pluginName?, limit?): ActivityLogEntry[]`
|
|
- Permission grants stored in a new `plugin_permissions` SQLite table
|
|
- Activity log stored in a new `plugin_activity_log` SQLite table
|
|
- [ ] Add `plugin_permissions` and `plugin_activity_log` tables to `src/main/db/schema.ts`
|
|
- [ ] Generate and apply migration
|
|
- **Files:** `src/main/permissions/manifest-validator.ts`, `src/main/permissions/permission-manager.ts`, `src/main/db/schema.ts`, `src/main/db/migrations/`
|
|
- **Outcome:** Granular, opt-in permission system for plugins. Every access is logged.
|
|
|
|
### Step 2.2 — Create worker pool and batch runner
|
|
- [ ] Create `src/main/workers/worker-pool.ts`:
|
|
- `WorkerPool` class:
|
|
- Manages a pool of Node.js `worker_threads`
|
|
- `runPlugin(manifest, context): Promise<PluginResult>` — spawns or reuses a worker, sends manifest + context, receives result
|
|
- Worker lifecycle: create, send message, receive result, terminate on timeout
|
|
- Max concurrent workers: configurable (default 4)
|
|
- Error isolation: worker crash doesn't affect main process
|
|
- [ ] Create `src/main/workers/batch-runner.ts`:
|
|
- `BatchRunner` class:
|
|
- `registerPlugin(manifest): void` — validates manifest, stores in registry
|
|
- `startScheduler(): void` — cron-based scheduler using `node-cron` or simple setInterval
|
|
- `runPlugin(name, triggerContext?): Promise<PluginResult>` — manual trigger
|
|
- `stopAll(): void` — graceful shutdown of all scheduled plugins
|
|
- Scheduler checks permissions before each run; skips if revoked
|
|
- Results logged to activity log
|
|
- [ ] Create `src/main/workers/plugin-worker.ts`:
|
|
- Worker thread entry point
|
|
- Receives plugin config + context via `parentPort.on('message')`
|
|
- Dynamically imports the plugin entry point
|
|
- Executes `run(context)` with sandboxed access (only permitted resources)
|
|
- Posts result back via `parentPort.postMessage()`
|
|
- **Files:** `src/main/workers/worker-pool.ts`, `src/main/workers/batch-runner.ts`, `src/main/workers/plugin-worker.ts`
|
|
- **Outcome:** Isolated plugin execution environment with scheduling, permissions enforcement, and error isolation.
|
|
|
|
### Step 2.3 — Implement batch agent plugins
|
|
- [ ] Create `src/plugins/email-scanner.ts`:
|
|
- Manifest: requires `read_email` permission
|
|
- Connects to IMAP via `imapflow` (account configured in settings)
|
|
- Scans for new emails since last run
|
|
- Uses `LiteLLMClient` to classify each email (has actionable task? extract title, priority, description)
|
|
- Returns extracted task metadata (never raw email content) for execution via backend or local playbook
|
|
- [ ] Create `src/plugins/file-watcher.ts`:
|
|
- Manifest: requires `read_folder` permission for each watched path
|
|
- Uses `chokidar` to watch approved directories
|
|
- On new/modified file: reads content, generates embedding, upserts into vector store
|
|
- Supports: .txt, .md, .pdf (text extraction), .docx (basic extraction)
|
|
- [ ] Create `src/plugins/calendar-sync.ts`:
|
|
- Manifest: requires `read_calendar` permission
|
|
- Parses ICS files or connects to CalDAV endpoint
|
|
- Detects scheduling conflicts
|
|
- Suggests reorganizations via LLM analysis
|
|
- Returns calendar events + conflict reports
|
|
- [ ] Create `src/plugins/browser-agent.ts`:
|
|
- Manifest: requires `read_browser_history` permission (explicit opt-in)
|
|
- Reads browser bookmarks and history from known browser paths (Chrome, Firefox, Edge)
|
|
- Indexes relevant entries into vector store
|
|
- Privacy-first: only indexes URLs and titles, not page content
|
|
- **Files:** `src/plugins/email-scanner.ts`, `src/plugins/file-watcher.ts`, `src/plugins/calendar-sync.ts`, `src/plugins/browser-agent.ts`
|
|
- **Outcome:** Four local batch agents running as isolated worker threads, using LiteLLM for analysis.
|
|
|
|
---
|
|
|
|
## Phase 3 — Backend Integration
|
|
|
|
### Step 3.1 — Create backend HTTP/WebSocket client
|
|
- [ ] Create `src/main/api/backend-client.ts`:
|
|
- `BackendClient` class:
|
|
- `baseUrl` configurable (default: production cloud URL, overridable for dev)
|
|
- `setAuthToken(jwt: string): void`
|
|
- `chat(request: ChatRequest): Promise<ChatResponse>` — POST /api/v1/chat
|
|
- `chatStream(request: ChatRequest): AsyncGenerator<string>` — WebSocket /api/v1/chat/stream
|
|
- `getPlaybooks(): Promise<ExecutionPlan[]>` — GET /api/v1/plans/playbook
|
|
- `uploadBackup(blob: Buffer, metadata: BackupMetadata): Promise<void>` — PUT /api/v1/backup
|
|
- `downloadBackup(): Promise<{ blob: Buffer, metadata: BackupMetadata }>` — GET /api/v1/backup
|
|
- Automatic retry with exponential backoff (max 3 attempts)
|
|
- Offline detection: returns cached playbook responses when offline
|
|
- `isOnline(): boolean` — connectivity check
|
|
- [ ] Create `src/main/api/plan-runner.ts`:
|
|
- `PlanRunner` class:
|
|
- `execute(plan: ExecutionPlan): Promise<PlanResult>` — executes plan steps locally
|
|
- Step handlers: `create_record` (inserts into SQLite), `update_record`, `delete_record`, `index_document` (upserts into vector store), `send_notification` (Electron notification API)
|
|
- Each step logs to activity log
|
|
- Supports `data_from_step` references (pipeline execution)
|
|
- Validates plan structure before execution
|
|
- **Files:** `src/main/api/backend-client.ts`, `src/main/api/plan-runner.ts`
|
|
- **Outcome:** Electron can communicate with the cloud backend and execute returned plans locally.
|
|
|
|
### Step 3.2 — Refactor orchestrator to delegate to backend
|
|
- [ ] Update `src/main/ai/orchestrator.ts`:
|
|
- When online: forward chat requests to backend via `BackendClient.chatStream()`
|
|
- Build `ChatRequest` from local context: query SQLite for user profile, relevant documents (from vector store), recent tasks, conversation history
|
|
- Stream backend response tokens to renderer via existing `ai:stream` IPC channel
|
|
- Execute any returned actions via `PlanRunner`
|
|
- When offline: fall back to local orchestration (existing LangGraph pipeline) with degraded capabilities
|
|
- Remove direct agent logic (project agent, knowledge agent, general agent tool definitions) — these now live on the backend
|
|
- Keep `buildProjectContext()` and `buildGlobalContext()` as context builders for the request payload
|
|
- [ ] Update `src/main/router/index.ts` `ai` sub-router:
|
|
- `chat` mutation: call refactored orchestrator (which now delegates to backend)
|
|
- Add `getPlaybooks` query: fetches cached playbooks
|
|
- Keep `dailyBrief` mutation: sends daily brief request to backend
|
|
- [ ] Add IPC handler for plan execution results
|
|
- **Files:** `src/main/ai/orchestrator.ts`, `src/main/router/index.ts`, `src/main/ipc.ts`
|
|
- **Outcome:** Chat intelligence lives on the backend; Electron is the execution layer.
|
|
|
|
### Step 3.3 — Implement Shared Memory (three-tier local memory)
|
|
- [ ] Create `src/main/database/shared-memory.ts`:
|
|
- **Short-term memory**: In-memory conversation buffer
|
|
- `ConversationBuffer` class: stores last N messages per session
|
|
- `addMessage(sessionId, role, content)`, `getHistory(sessionId, limit?) -> Message[]`
|
|
- Cleared on session end
|
|
- **Long-term KV store**: SQLite-backed key-value store
|
|
- New `agent_memory` table: `id`, `namespace` (agent name), `key`, `value` (JSON text), `updated_at`
|
|
- `AgentMemoryStore` class: `get(namespace, key)`, `set(namespace, key, value)`, `delete(namespace, key)`, `listKeys(namespace)`
|
|
- Used by agents to persist learned facts, user preferences
|
|
- **Vector store**: Already exists (LanceDB). Enhance with:
|
|
- Multi-collection support: separate tables for notes, emails, files, calendar
|
|
- `searchByCollection(collection, query, limit) -> SearchResult[]`
|
|
- [ ] Add `agent_memory` table to `src/main/db/schema.ts`
|
|
- [ ] Generate migration
|
|
- **Files:** `src/main/database/shared-memory.ts`, `src/main/db/schema.ts`, `src/main/db/migrations/`
|
|
- **Outcome:** Three-tier memory system supporting short-term conversation, long-term agent facts, and semantic search.
|
|
|
|
---
|
|
|
|
## Phase 4 — Security: E2E Backup & Offline Mode
|
|
|
|
### Step 4.1 — Implement E2E encrypted backup
|
|
- [ ] Create `src/main/backup/e2e-crypto.ts`:
|
|
- `generatePassphrase(): string` — BIP39-compatible 12-word recovery phrase
|
|
- `deriveKey(passphrase: string, salt: Buffer): Promise<Buffer>` — Argon2id key derivation (time cost 3, memory 64MB, parallelism 1)
|
|
- `encrypt(data: Buffer, key: Buffer): { ciphertext: Buffer, iv: Buffer, authTag: Buffer }` — AES-256-GCM
|
|
- `decrypt(ciphertext: Buffer, key: Buffer, iv: Buffer, authTag: Buffer): Buffer`
|
|
- Uses `node:crypto` for AES and `argon2` npm package for key derivation
|
|
- [ ] Create `src/main/backup/backup-manager.ts`:
|
|
- `BackupManager` class:
|
|
- `createBackup(passphrase: string): Promise<BackupBlob>` — Exports SQLite DB, encrypts, returns blob + metadata
|
|
- `restoreBackup(blob: Buffer, passphrase: string): Promise<void>` — Decrypts blob, replaces local DB, re-initializes
|
|
- `uploadBackup(passphrase: string): Promise<void>` — Creates backup, uploads via `BackendClient`
|
|
- `downloadAndRestore(passphrase: string): Promise<void>` — Downloads from backend, decrypts, restores
|
|
- Incremental backup: chunks DB into segments, encrypts each separately, tracks content hashes to skip unchanged chunks
|
|
- Metadata header: version, timestamp, checksum (SHA-256 of plaintext), chunk count
|
|
- **Files:** `src/main/backup/e2e-crypto.ts`, `src/main/backup/backup-manager.ts`
|
|
- **Outcome:** User data never leaves the device unencrypted. Backend stores only opaque blobs.
|
|
|
|
### Step 4.2 — Implement offline sync queue
|
|
- [ ] Create `src/main/backup/sync-queue.ts`:
|
|
- `SyncQueue` class:
|
|
- `enqueue(action: QueuedAction): void` — Adds action to persistent queue (SQLite table `sync_queue`)
|
|
- `processQueue(): Promise<void>` — Processes queued actions in FIFO order when online
|
|
- `getQueueSize(): number`
|
|
- `clearQueue(): void`
|
|
- Conflict resolution: last-write-wins with timestamps
|
|
- New `sync_queue` table: `id`, `action_type`, `payload` (JSON), `created_at`, `status` (pending/processing/failed), `retry_count`, `last_error`
|
|
- Auto-drain: watches connectivity, starts processing when online
|
|
- Failed actions: retry up to 3 times with exponential backoff, then mark as `failed` for user review
|
|
- [ ] Add `sync_queue` table to schema
|
|
- [ ] Integrate with `BackendClient`: when offline, chat/backup calls enqueue instead of failing
|
|
- **Files:** `src/main/backup/sync-queue.ts`, `src/main/db/schema.ts`, `src/main/api/backend-client.ts`
|
|
- **Outcome:** App works offline; queued actions sync automatically when connectivity returns.
|
|
|
|
---
|
|
|
|
## Phase 5 — Auth Integration & Database Encryption
|
|
|
|
### Step 5.1 — Integrate auth into Electron app
|
|
- [ ] Create `src/main/auth/auth-manager.ts`:
|
|
- `AuthManager` class:
|
|
- `login(email, password): Promise<void>` — Calls backend POST /api/v1/auth/login, stores JWT in secure storage (via token.ts)
|
|
- `register(email, password): Promise<void>` — Calls POST /api/v1/auth/register
|
|
- `logout(): void` — Clears stored JWT
|
|
- `getToken(): string | null` — Returns current JWT
|
|
- `refreshToken(): Promise<void>` — Auto-refresh before expiry
|
|
- `isAuthenticated(): boolean`
|
|
- `getCurrentTier(): BillingTier`
|
|
- Auto-refresh: checks token expiry every 5 minutes, refreshes if < 10 minutes remaining
|
|
- [ ] Add tRPC procedures: `auth.login`, `auth.register`, `auth.logout`, `auth.status`, `auth.tier`
|
|
- [ ] Wire `BackendClient` to use `AuthManager.getToken()` for all requests
|
|
- **Files:** `src/main/auth/auth-manager.ts`, `src/main/router/index.ts`, `src/main/api/backend-client.ts`
|
|
- **Outcome:** Electron app has full auth flow; backend requests are authenticated.
|
|
|
|
### Step 5.2 — Migrate from better-sqlite3 to SQLCipher
|
|
- [ ] Add `@journeyapps/sqlcipher` to dependencies (replaces `better-sqlite3`)
|
|
- [ ] Update `src/main/db/index.ts`:
|
|
- Replace `better-sqlite3` import with `@journeyapps/sqlcipher`
|
|
- On first launch: derive DB key from OS keychain or prompt user
|
|
- `initDb(password)`: opens DB with `PRAGMA key = 'password'`
|
|
- Migration path for existing unencrypted DBs: detect → export → create encrypted → import → delete old
|
|
- WAL mode still enabled after keying
|
|
- [ ] Update `src/main/index.ts`: pass password to `initDb()`
|
|
- [ ] Test that all existing Drizzle operations work with SQLCipher
|
|
- **Files:** `package.json`, `src/main/db/index.ts`, `src/main/index.ts`
|
|
- **Outcome:** All local data encrypted at rest with SQLCipher.
|
|
|
|
---
|
|
|
|
## Phase 6 — Renderer UI Updates
|
|
|
|
### Step 6.1 — Update Settings page for multi-provider config
|
|
- [ ] Add provider management UI to Settings:
|
|
- List of configured providers with status (active/inactive/error)
|
|
- Add provider form: name dropdown (OpenAI, Anthropic, Google, Mistral, Groq, Ollama), API key input, model selection, endpoint (for Ollama)
|
|
- Set primary and fallback providers
|
|
- Test connection button per provider
|
|
- [ ] Add auth section to Settings:
|
|
- Login/register form
|
|
- Current tier display with upgrade CTA
|
|
- Logout button
|
|
- [ ] Add backup section to Settings:
|
|
- Create/view recovery passphrase
|
|
- Manual backup trigger
|
|
- Backup history with restore points
|
|
- Auto-backup schedule toggle
|
|
- **Files:** `src/renderer/components/settings/` (new), route file
|
|
- **Outcome:** Users can manage AI providers, auth, and backups from Settings.
|
|
|
|
### Step 6.2 — Add Permission Dialog and Activity Log
|
|
- [ ] Create `src/renderer/components/permissions/PermissionDialog.tsx`:
|
|
- Modal shown when a plugin requests new permissions
|
|
- Lists requested permissions with reasons
|
|
- Per-permission approve/deny toggles
|
|
- Shows plugin manifest info (name, description, version)
|
|
- [ ] Create `src/renderer/components/permissions/ActivityLog.tsx`:
|
|
- Filterable table of all plugin activity
|
|
- Columns: timestamp, plugin name, action type, resource, status
|
|
- Filter by plugin, date range, action type
|
|
- Export as CSV
|
|
- [ ] Add tRPC procedures for permission management and activity log queries
|
|
- **Files:** `src/renderer/components/permissions/PermissionDialog.tsx`, `src/renderer/components/permissions/ActivityLog.tsx`, `src/main/router/index.ts`
|
|
- **Outcome:** Transparent permission system with full activity audit trail.
|
|
|
|
### Step 6.3 — Update AIChatPanel for backend-powered chat
|
|
- [ ] Update `src/renderer/hooks/useAIChat.ts`:
|
|
- Support WebSocket streaming from backend (when online)
|
|
- Fall back to IPC streaming (when offline, using local orchestrator)
|
|
- Add connection status indicator (online/offline/reconnecting)
|
|
- Support execution plan responses: show plan preview, allow user to approve/modify before execution
|
|
- [ ] Update `src/renderer/components/ai/AIChatPanel.tsx`:
|
|
- Add connection status badge
|
|
- Add tier indicator (shows current plan limitations)
|
|
- Plan approval UI: expandable plan steps with approve/reject buttons
|
|
- Enhanced error states: differentiate between offline, auth expired, rate limited, server error
|
|
- [ ] Update `src/renderer/components/ai/FloatingChat.tsx`:
|
|
- Same streaming changes as AIChatPanel
|
|
- Compact plan approval for inline context
|
|
- **Files:** `src/renderer/hooks/useAIChat.ts`, `src/renderer/components/ai/AIChatPanel.tsx`, `src/renderer/components/ai/FloatingChat.tsx`
|
|
- **Outcome:** Chat UI seamlessly handles both online (backend) and offline (local) modes.
|
|
|
|
---
|
|
|
|
## Phase 7 — Cleanup & Hardening
|
|
|
|
### Step 7.1 — Remove deprecated AI code
|
|
- [ ] Delete `src/main/ai/copilot.ts` (Copilot SDK replaced by LiteLLM)
|
|
- [ ] Delete `src/main/ai/chat-copilot.ts` (LangChain adapter no longer needed)
|
|
- [ ] Delete or archive `src/main/ai/llm.ts` (replaced by `src/main/llm/litellm-client.ts`)
|
|
- [ ] Remove `@github/copilot-sdk`, `@langchain/langgraph` from dependencies (if unused)
|
|
- [ ] Clean up `src/main/ai/provider.ts`: simplify to delegate to `src/main/llm/providers.ts`
|
|
- [ ] Remove `currentSender` module-level mutable state from orchestrator (proper context passing)
|
|
- [ ] Update `src/main/index.ts` startup: remove `import './ai/copilot'`, add `BatchRunner.startScheduler()`, add `AuthManager` init
|
|
- **Files:** Multiple files under `src/main/ai/`, `package.json`, `src/main/index.ts`
|
|
- **Outcome:** No dead code; clean, maintainable codebase.
|
|
|
|
### Step 7.2 — Add error handling and logging
|
|
- [ ] Implement structured logging in main process:
|
|
- Log levels: debug, info, warn, error
|
|
- Log destinations: console (dev), file (production, rotated)
|
|
- Correlation IDs for request tracing across IPC → backend → response
|
|
- [ ] Add error boundaries in renderer:
|
|
- Per-route error boundaries
|
|
- AI chat error boundary (graceful degradation)
|
|
- Plugin error boundary (shows which plugin failed)
|
|
- **Files:** `src/main/utils/logger.ts` (new), `src/renderer/components/ErrorBoundary.tsx` (new)
|
|
- **Outcome:** Production-ready error handling and observability.
|
|
|
|
### Step 7.3 — Electron integration tests
|
|
- [ ] Test BackendClient with mocked HTTP responses
|
|
- [ ] Test PlanRunner with sample execution plans
|
|
- [ ] Test SyncQueue offline → online transition
|
|
- [ ] Test BackupManager encrypt → decrypt round-trip
|
|
- [ ] Test PermissionManager grant → check → revoke cycle
|
|
- **Files:** `src/main/__tests__/` (new test directory)
|
|
- **Outcome:** Confidence that all Electron-side components work correctly.
|
|
|
|
---
|
|
|
|
## New Dependencies (package.json)
|
|
|
|
| Package | Purpose |
|
|
|---|---|
|
|
| `@journeyapps/sqlcipher` | Encrypted SQLite (replaces `better-sqlite3`) |
|
|
| `argon2` | Key derivation for E2E backup |
|
|
| `node-cron` | Batch agent scheduling |
|
|
| `chokidar` | File watching (FileWatcher plugin) |
|
|
| `imapflow` | IMAP client (EmailScanner plugin) |
|
|
| `onnxruntime-node` | Local embeddings (optional) |
|
|
|
|
---
|
|
|
|
## Execution Notes
|
|
|
|
- **Each step is independently committable** and produces working code.
|
|
- **Phases 1-2** (LLM client + plugins) are independent of the backend — can start immediately.
|
|
- **Phase 3** (backend integration) requires the backend repo to have the `/api/v1/chat` endpoint ready.
|
|
- **Phase 5.2** (SQLCipher) is intentionally late to avoid encryption overhead during active schema changes.
|
|
- **The existing app continues to work** throughout the migration. Local orchestration is preserved until backend is ready (Step 3.2).
|