
### Fixed
- **Sleep/wake reconnection no longer silently drops messages** — after laptop sleep/wake (or any tab visibility change), the WebSocket reconnects but messages could stop appearing in the chat. The root cause was a cascade: disposing the old client set `sdkClient = null`, which caused session hooks to clear the session list, which cleared `currentSessionIdRef`, which prevented session resume and caused all incoming `message.created` / `part.append` events to be silently ignored.
  - `useWorkspaceSessions` and `useOverviewSessions` now keep stale session data when `sdkClient` becomes `null` during a reconnection, instead of clearing sessions to `[]`.
  - The `'connected'` handler in `useConnectionLifecycle` now falls back to reading the session ID from the session store when `currentSessionIdRef.current` is `null`, and patches the ref immediately so subsequent event handlers see the correct value.
- **Proactive zombie WebSocket disposal on tab wake** — the `visibilitychange` handler now always disposes the existing WebSocket and forces a fresh reconnection when the tab becomes visible, rather than trusting `readyState === OPEN` (which can be stale after OS sleep). This prevents a zombie state where the UI appears connected but the underlying TCP connection is dead.

### Changed
- **Pre-flight auth verification before WebSocket connection** — the client now makes a lightweight `GET /api/auth/verify` HTTP request before opening a WebSocket. If the token is invalid (401), the client immediately surfaces a clear "Authentication failed" error to the user instead of entering an infinite retry loop with a confusing "Connection timed out" message. If the server is unreachable (network error), the client falls through to the existing WebSocket retry logic unchanged.
- **`retry()` clears `authError`** — manually retrying a connection (e.g. after entering a corrected token) now clears the stored auth error, so the UI doesn't show a stale error message.
