Normal authentication failures (401 Unauthorized, 403 Forbidden, session
expired) are not backend errors — they simply mean the user isn't logged in.
Previously these fell through to the `instanceof Error` catch-all and returned
"backend", causing a misleading "having trouble connecting" banner.
Now classifyAuthError explicitly checks for invalid_credentials and
session_expired codes from parseAuthError and returns null, so the UI shows
the logged-out state cleanly without an error banner.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add test for non-string error.message fallback in handleCredentialsLogin.
Rename misleading refreshSession test to match actual behavior.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Verify verifySession returns null when getSession throws non-Error
values (strings, objects) rather than crashing.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
fetchWithRetry now clamps maxRetries>=0, baseDelayMs>=100,
backoffFactor>=1 to prevent infinite loops or zero-delay hammering.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Update .env.example to list all 4 required OIDC vars (was missing OIDC_REDIRECT_URI).
Fix test assertion to match username->email rename in signInWithCredentials.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Eliminates manual duplication of AuthErrorCode values in KNOWN_CODES
by deriving from Object.keys(ERROR_MESSAGES).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Wire fetchWithRetry into login page config fetch (was dead code)
- Remove duplicate ERROR_CODE_MESSAGES, use parseAuthError from auth-errors.ts
- Fix OAuth sign-in fire-and-forget: add .catch() with PDA error + loading reset
- Fix credential login catch: use parseAuthError for better error messages
- Add user feedback when auth config fetch fails (was silent degradation)
- Fix sign-out failure: use logAuthError and set authError state
- Enable fetchWithRetry production logging for retry visibility
Co-Authored-By: Claude Opus 4.6 <[email protected]>
BetterAuth defaulted basePath to /api/auth but NestJS controller routes
to /auth/* (no global prefix). The auth client also pointed at the web
frontend origin instead of the API server, and LoginButton used a
nonexistent GET /auth/signin/authentik endpoint.
- Set basePath: "/auth" in BetterAuth server config
- Point auth client baseURL to API_BASE_URL with matching basePath
- Add genericOAuthClient plugin to auth client
- Use signIn.oauth2({ providerId: "authentik" }) in LoginButton
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Implements the SpeechSettings component with four sections:
- STT settings (enable/disable, language preference)
- TTS settings (enable/disable, voice selector, tier preference, auto-play, speed control)
- Voice preview with test button
- Provider status with health indicators
Also adds Slider UI component and getHealthStatus API client function.
30 unit tests covering all sections, toggles, voice loading, and PDA-friendly design.
Fixes#404
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Implements AudioPlayer inline component with play/pause, progress bar,
speed control (0.5x-2x), download, and duration display. Adds
TextToSpeechButton "Read aloud" component that synthesizes text via
the speech API and integrates AudioPlayer for playback. Includes
useTextToSpeech hook with API integration, audio caching, and
playback state management. All 32 tests passing.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Fix Tooltip formatter/labelFormatter type overload conflicts
- Fix Pie label render props type mismatch
- Fix telemetry.ts date split array access type
Refs #375
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Install recharts for data visualization
- Add Usage nav item to sidebar navigation
- Create telemetry API service with data fetching functions
- Build dashboard page with summary cards, charts, and time range selector
- Token usage line chart, cost breakdown bar chart, task outcome pie chart
- Loading and empty states handled
- Responsive layout with PDA-friendly design
- Add unit tests (14 tests passing)
Refs #375
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Port high-value features from work/m4-llm branch into develop's
security-hardened codebase:
- Separate LLM vs persistence error handling in useChat (shows
assistant response even when save fails)
- Add structured error context logging with errorType, messagePreview,
messageCount fields for debugging
- Enforce state invariant in useChatOverlay: cannot be minimized when
closed
- Add onStorageError callback with user-friendly messages and
per-error-type deduplication
- Add error logging to Chat imperative handle methods
- Create Chat.test.tsx with loadConversation failure mode tests
Skipped from work/m4-llm (superseded by develop):
- AbortSignal timeout (develop has centralized client timeout)
- Custom toast system (duplicates @mosaic/ui)
- ErrorBoundary (develop has its own)
- WebSocket typed events (develop's ref-based pattern is superior)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace exported const mockConnections with getMockConnections() function
that returns mock data only when NODE_ENV === "development". In production
and test environments, returns an empty array as defense-in-depth alongside
the existing ComingSoon page gate (SEC-WEB-4).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
SEC-WEB-33: Replace raw diagram source and detailed error messages in
MermaidViewer error UI with a generic "Diagram rendering failed" message.
Detailed errors are logged to console.error for debugging only.
SEC-WEB-35: Add console.warn in useWorkspaceId when no workspace ID is
found in localStorage, making it easier to distinguish "no workspace
selected" from silent hook failure.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
SEC-WEB-32: Added maxLength to form inputs (names: 100, descriptions: 500,
emails: 254) in WorkspaceSettings, TeamSettings, InviteMember components.
SEC-WEB-34: Added AbortController timeout (30s default, configurable) to
apiRequest and apiPostFormData in API client.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add runtime type validation after all JSON.parse calls in the web app to
prevent runtime crashes from corrupted or tampered storage data. Creates a
shared safeJsonParse utility with type guard functions for each data shape
(Message[], ChatOverlayState, LayoutConfigRecord). All four affected
callsites now validate parsed data and fall back to safe defaults on
mismatch.
Files changed:
- apps/web/src/lib/utils/safe-json.ts (new utility)
- apps/web/src/lib/utils/safe-json.test.ts (25 tests)
- apps/web/src/hooks/useChat.ts (deserializeMessages)
- apps/web/src/hooks/useChat.test.ts (3 new corruption tests)
- apps/web/src/hooks/useChatOverlay.ts (loadState)
- apps/web/src/hooks/useChatOverlay.test.ts (3 new corruption tests)
- apps/web/src/components/chat/ConversationSidebar.tsx (ideaToConversation)
- apps/web/src/lib/hooks/useLayout.ts (layout loading)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Create centralized config module (apps/web/src/lib/config.ts) exporting:
- API_BASE_URL: Main API server URL from NEXT_PUBLIC_API_URL
- ORCHESTRATOR_URL: Orchestrator service URL from NEXT_PUBLIC_ORCHESTRATOR_URL
- Helper functions for building full URLs
- Update client.ts to import from central config
- Update LoginButton.tsx to use API_BASE_URL from config
- Update useWebSocket.ts to use API_BASE_URL from config
- Update AgentStatusWidget.tsx to use ORCHESTRATOR_URL from config
- Update TaskProgressWidget.tsx to use ORCHESTRATOR_URL from config
- Update useGraphData.ts to use API_BASE_URL from config
- Fixed wrong default port (was 8000, now uses correct 3001)
- Add comprehensive tests for config module
- Update useWebSocket tests to properly mock config module
Refs #338
Co-Authored-By: Claude Opus 4.5 <[email protected]>