AGENTS.md@apps/desktop · diff

git:20260721.483d9ae to git:20260726.74d805c

23 added, 127 removed. Audit A to A.

- # Desktop App Guidelines
-
- The Cowork Desktop App is an Electron + React + TypeScript application that provides a native GUI for the agent-coworker server. It manages workspaces, threads, and chat sessions via WebSocket connections to the core server.
-
- ## Project Structure & Module Organization
-
- - `src/`: Renderer React application
- - `src/App.tsx`: Root component with topbar controls and view routing
- - `src/main.tsx`: Renderer entry point
- - `src/app/`: State management and types
- - `store.ts`: Zustand store with app state, WebSocket management, and server lifecycle
- - `types.ts`: TypeScript interfaces for threads, workspaces, feed items, etc.
- - `src/ui/`: React components
- - `src/lib/`: Renderer utilities and desktop bridge wrappers
- - `desktopApi.ts`: Shared IPC contract/channel names
- - `desktopCommands.ts`: Renderer wrappers for preload APIs
- - `agentSocket.ts`: WebSocket wrapper with reconnect behavior
- - `electron/`: Electron main/preload process code
- - `main.ts`: BrowserWindow lifecycle
- - `preload.ts`: Context-isolated bridge exposed to renderer (`window.cowork`)
- - `ipc.ts`: IPC handler registration
- - `services/`: Main-process services (server process manager, persistence)
- - `test/`: Bun tests (`*.test.ts`)
- - `electron.vite.config.ts`: Electron build config (main, preload, renderer)
- - `electron-builder.yml`: macOS/Windows packaging configuration
-
- ## Build, Test, and Development Commands
-
- From the desktop app directory:
-
- - `bun install`: Install dependencies
- - `bun run dev`: Run full Electron app in dev mode
- - `bun run build`: Build distributables (macOS/Windows)
- - `bun run test`: Run desktop tests
-
- The desktop app depends on the core server resources. Scripts run `bun run --cwd ../.. build:desktop-resources` to rebuild sidecar + bundled assets.
-
- ## Coding Style & Naming Conventions
-
- - TypeScript strict mode enabled
- - 2-space indentation
- - Prefer `camelCase` for values, `PascalCase` for types
- - Group imports: Node built-ins, third-party, local aliases (`@cowork/*`), relative imports
- - React functional components with hooks
- - Zustand single-store pattern
-
- ## Architecture Notes
-
- ### WebSocket Communication
-
- The desktop app maintains two WebSocket connection types to the agent-coworker server:
-
- 1. **Control Socket**: Per-workspace connection for management operations (skills, provider config)
- 2. **Thread Sockets**: Per-thread connections for chat sessions
-
- All WebSocket logic is in `src/app/store.ts`. The `AgentSocket` class in `src/lib/agentSocket.ts` handles connection lifecycle with exponential backoff.
-
- ### State Persistence
-
- State (workspaces/threads) and per-thread transcript JSONL files are persisted by Electron main-process services. Renderer code accesses these through the preload bridge in `window.cowork` and `src/lib/desktopCommands.ts`.
-
- ### Server Lifecycle
-
- Each workspace runs its own `cowork-server` process:
-
- 1. User selects workspace → `selectWorkspace()`
- 2. Renderer requests `startWorkspaceServer()` via IPC
- 3. Main process starts/reuses workspace server and returns websocket URL
- 4. Renderer opens control + thread sockets against that URL
-
- ### Protocol
-
- Desktop renderer WebSocket traffic uses JSON-RPC over `cowork.jsonrpc.v1`. Shared JSON-RPC schemas live under `src/server/jsonrpc/`; internal session event payload types are re-exported from `src/server/protocol.ts`.
-
- ## Security & Configuration
-
- - `BrowserWindow` runs with `contextIsolation: true`, `nodeIntegration: false`
- - Do not expose unrestricted Node APIs to renderer; all privileged actions go through explicit preload methods
- - API keys are handled by the core server, not desktop UI
- - Never log sensitive workspace paths, API keys, or session data in UI
-
- ## Common Tasks
-
- ### Adding a new WebSocket message type
-
- 1. Add the JSON-RPC schema/route in `src/server/jsonrpc/`
- 2. Add renderer handling in `src/app/store.ts` or the focused helper module
- 3. Update protocol re-exports if needed in `src/lib/wsProtocol.ts`
-
- ### Adding a new desktop command
-
- 1. Add a new channel/method in `src/lib/desktopApi.ts`
- 2. Expose the method in `electron/preload.ts`
- 3. Implement IPC handler in `electron/ipc.ts`
- 4. Add renderer wrapper in `src/lib/desktopCommands.ts`
-
- ### Styling Guidelines
+ # Desktop App (apps/desktop)
- - Use shadcn/ui specifically for desktop renderer UI. Do not add HeroUI or another component library.
- - The shadcn config is `components.json`: Vite, Tailwind v4, radix base, lucide icons, `@/components/ui/*` imports, and `@/lib/utils`.
- - Run shadcn commands from `apps/desktop` with Bun: `bunx --bun shadcn@latest info --json`, `bunx --bun shadcn@latest docs <component>`, and `bunx --bun shadcn@latest add <component>`.
- - Prefer existing shadcn primitives in `src/components/ui` before custom markup. The full registry component set should be present there, alongside any app-specific local components.
- - Compose components using shadcn conventions: full `Card` structure, `Dialog`/`Sheet` titles, `TabsTrigger` inside `TabsList`, `SelectItem` inside groups where applicable, `Field`/`FieldGroup` and `InputGroup` for form layout, `Separator`/`Skeleton`/`Badge` instead of custom one-off divs.
- - Style with semantic CSS tokens from `src/styles.css`, component variants, `gap-*` instead of `space-*`, `size-*` for equal dimensions, and `cn()` for conditional classes.
- - Use lucide icons with `data-icon="inline-start"` or `data-icon="inline-end"` inside `Button`; let the component own icon sizing.
- - Use the shared `Switch` for binary settings and reserve `Checkbox` for checklist selection.
- - Preview updates to existing components with `--dry-run` or `--diff`; do not overwrite desktop-customized wrappers without checking the diff and running relevant tests.
+ Electron + React + TypeScript GUI for the agent-coworker server. This is a UI layer only — business logic lives in the harness/server and is consumed over JSON-RPC WebSocket (see root `AGENTS.md`). Read `../agent_docs/desktop-ui.md` before UI or Electron work.
- ## Code Review Rules
+ ## Layout
- Apply these rules only to diffs that touch the relevant desktop surface. Report
- only concrete violations introduced by the change.
+ - `src/` — renderer React app: `src/app/store.ts` (Zustand store, WebSocket management), `src/ui/` (components), `src/lib/` (`desktopApi.ts` IPC contract, `desktopCommands.ts` preload wrappers, `agentSocket.ts` reconnecting WS client)
+ - `electron/` — main/preload: `main.ts` (window lifecycle), `preload.ts` (context-isolated `window.cowork` bridge), `ipc.ts` (handlers), `services/` (server process manager, persistence)
+ - `src/components/ui/` — shadcn/ui component set
+ - `test/` — Bun tests (`*.test.ts`)
- ### Privileged IPC
+ ## Commands
- - Search for a renderer capability that bypasses the typed preload bridge, or an
- IPC contract changed in only part of the call chain. Keep the channel and
- payload types, preload exposure, main-process handler, and renderer wrapper in
- sync. Validate the sender and inputs in the main process; never expose an
- unrestricted Node or filesystem primitive to the renderer.
+ From the repo root:
- ### Workspace persistence
+ - `bun run desktop:dev` — dev mode (builds sidecar resources first, then `electron-vite dev`)
+ - `bun run typecheck:desktop` — desktop TypeScript check
+ - `bun run test -- apps/desktop/test` — desktop tests
+ - `bun run desktop:build` — distributables (macOS/Windows)
- - Search for new, renamed, or cleared workspace fields that do not round-trip
- through persistence sanitization and migration. Preserve explicit
- clear/inherit semantics, mutate the same workspace class the control renders,
- and add save/reload coverage so a setting cannot appear to work and then
- silently disappear after restart.
+ ## Invariants
- ### Optimistic message identity
+ - Each workspace runs its own `cowork-server` process; the renderer opens a per-workspace control socket plus per-thread chat sockets against it.
+ - `BrowserWindow` runs with `contextIsolation: true`, `nodeIntegration: false`. All privileged actions go through explicit preload methods; validate sender and inputs in the main process. Never expose unrestricted Node APIs to the renderer.
+ - API keys are handled by the core server, never the desktop UI. Never log sensitive workspace paths, keys, or session data.
+ - New desktop command: channel in `src/lib/desktopApi.ts` → expose in `electron/preload.ts` → handler in `electron/ipc.ts` → renderer wrapper in `src/lib/desktopCommands.ts`. Keep all four in sync.
+ - New WebSocket message: schema/route in `src/server/jsonrpc/` first, then renderer handling in `src/app/store.ts`; update `docs/websocket-protocol.md`.
+ - Verify UI changes against the live running app via the CDP workflow (`COWORK_ELECTRON_REMOTE_DEBUG=1`) — tests alone are not proof.
- - Search for chat send, retry, steer, replay, or projection changes that drop or
- regenerate `clientMessageId`. Preserve the originating ID through JSON-RPC,
- server events, projected user items, retries, and reconciliation so an
- accepted send cannot render duplicate user messages.
+ ## Read when relevant
- <!-- HEROUI-REACT-AGENTS-MD-END -->
+ - `../agent_docs/desktop-ui.md` — shadcn/ui rules, Electron tooling, desktop UI patterns
+ - `../agent_docs/code-review-rules.md` — IPC / persistence / message-identity review rules