- Mobile-first responsive classes (p-4 sm:p-8, text-2xl sm:text-4xl)
- WCAG 2.1 AA: role=status on loading spinner, aria-labels, focus management
- Loading spinner has role=status and aria-label
- All interactive elements keyboard-accessible
- Added 10 new tests for responsive layout and accessibility
Refs #416
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maps error codes to PDA-friendly messages (no alarming language).
Dismissible error banner with URL param cleanup.
Refs #416
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LoginButton.tsx and LoginButton.test.tsx removed. The login page now
uses OAuthButton, LoginForm, and AuthDivider from the auth redesign.
Refs #416
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetches GET /auth/config on mount and renders OAuth + email/password
forms based on backend-advertised providers. Falls back to email-only
if config fetch fails.
Refs #416
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded production URLs with environment-driven config.
Reads NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_API_URL, TRUSTED_ORIGINS.
Localhost fallbacks only in development mode.
Refs #414
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies response body never contains CLIENT_SECRET, CLIENT_ID,
JWT_SECRET, BETTER_AUTH_SECRET, CSRF_SECRET, or issuer URLs.
Refs #413
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add getAuthConfig() to AuthService (email always, OIDC when enabled)
- Add GET /auth/config public endpoint with Cache-Control: 5min
- Place endpoint before catch-all to avoid interception
Refs #413
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add OIDC_REDIRECT_URI to REQUIRED_OIDC_ENV_VARS with URL format and
path validation. The redirect URI must be a parseable URL with a path
starting with /auth/callback. Localhost usage in production triggers
a warning but does not block startup.
This prevents 500 errors when BetterAuth attempts to construct the
authorization URL without a configured redirect URI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The global CsrfGuard blocks POST /auth/sign-in/oauth2 with 403 because
unauthenticated users have no session and therefore no CSRF token.
BetterAuth handles its own CSRF protection via toNodeHandler().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Docker/load-balancer health probes hit GET /health every ~5s from
127.0.0.1, exhausting the rate limit and causing all subsequent checks
to return 429 — making the service appear unhealthy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 <noreply@anthropic.com>
The constant-time comparison test used Date.now() deltas with a 10ms
threshold which is unreliable in CI. Replace with deterministic tests
that verify both same-length and different-length key rejection paths
work correctly. The actual timing-safe behavior is guaranteed by
Node's crypto.timingSafeEqual which the guard uses.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BetterAuth expects Web API Request objects (Fetch API standard) with
headers.get(), but NestJS/Express passes IncomingMessage objects with
headers[] property access. Use better-auth/node's toNodeHandler to
properly convert between Express req/res and BetterAuth's Web API handler.
Also fixes vitest SWC config to read the correct tsconfig for NestJS
decorator metadata emission, which was causing DI injection failures
in tests.
Fixes#410
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Alpine (musl libc) is incompatible with matrix-sdk-crypto-nodejs native binary
which requires glibc's ld-linux-x86-64.so.2. Switched all Node.js Dockerfiles
to node:24-slim (Debian/glibc). Also fixed docker-compose.matrix.yml network
naming from undefined mosaic-network to mosaic-internal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 0.1.0 package was ESM-only, causing ERR_PACKAGE_PATH_NOT_EXPORTED
when loaded by NestJS (which compiles to CommonJS). Version 0.1.1 ships
dual ESM/CJS builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: migration 20260129235248_add_link_storage_fields dropped the
personalities table and FormalityLevel enum, but migration
20260208000000_add_missing_tables later references personalities in a FK
constraint, causing ERROR: relation "personalities" does not exist on any
fresh database deployment.
Fix 1 — 20260208000000_add_missing_tables:
Recreate FormalityLevel enum and personalities table (with current schema
structure) at the top of the migration, before the FK constraint.
Fix 2 — New migration 20260215100000_fix_schema_drift:
- Create missing instances table (Federation module, never migrated)
- Recreate knowledge_links unique index (dropped, never recreated)
- Add 7 missing @@unique([id, workspaceId]) composite indexes
- Add missing agent_tasks.agent_type index
Verified: all 27 migrations apply cleanly on a fresh PostgreSQL 17 database
with pgvector.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep both Mosaic Telemetry section (from develop) and Matrix Dev
Environment section (from feature branch) in .env.example.
Regenerate pnpm-lock.yaml with both dependency trees merged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Docker build failed because pip couldn't find mosaicstack-telemetry
from the private Gitea PyPI registry. Copy pip.conf into the image so
pip resolves the extra-index-url during docker build.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical fixes:
- Fix FormData field name mismatch (audio -> file) to match backend FileInterceptor
- Add /speech namespace to WebSocket connection URL
- Pass auth token in WebSocket handshake options
- Wrap audio.play() in try-catch for NotAllowedError and DOMException handling
- Replace bare catch block with named error parameter and descriptive message
- Add connect_error and disconnect event handlers to WebSocket
- Update JSDoc to accurately describe batch transcription (not real-time partial)
Important fixes:
- Emit transcription-error before disconnect in gateway auth failures
- Capture MediaRecorder error details and clean up media tracks on error
- Change TtsDefaultConfig.format type from string to AudioFormat
- Define canonical SPEECH_TIERS and AUDIO_FORMATS arrays as single source of truth
- Fix voice count from 54 to 53 in provider, AGENTS.md, and docs
- Fix inaccurate comments (Piper formats, tier prop, SpeachesProvider, TextValidationPipe)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comprehensive documentation for the speech services module:
- docs/SPEECH.md: Architecture, API reference, WebSocket protocol,
environment variables, provider configuration, Docker setup,
GPU VRAM budget, and frontend integration examples
- apps/api/src/speech/AGENTS.md: Module structure, provider pattern,
how to add new providers, gotchas, and test patterns
- README.md: Speech capabilities section with quick start
Fixes#406
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
- Fix sendThreadMessage room mismatch: use channelId from options instead of hardcoded controlRoomId
- Add .catch() to fire-and-forget handleRoomMessage to prevent silent error swallowing
- Wrap dispatchJob in try-catch for user-visible error reporting in handleFixCommand
- Add MATRIX_BOT_USER_ID validation in connect() to prevent infinite message loops
- Fix streamResponse error masking: wrap finally/catch side-effects in try-catch
- Replace unsafe type assertion with public getClient() in MatrixRoomService
- Add orphaned room warning in provisionRoom on DB failure
- Add provider identity to Herald error logs
- Add channelId to ThreadMessageOptions interface and all callers
- Add missing env var warnings in BridgeModule factory
- Fix JSON injection in setup-bot.sh: use jq for safe JSON construction
Fixes#377
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SpeechGateway with Socket.IO namespace /speech for real-time
streaming transcription. Supports start-transcription, audio-chunk,
and stop-transcription events with session management, authentication,
and buffer size rate limiting. Includes 29 unit tests covering
authentication, session lifecycle, error handling, cleanup, and
client isolation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SpeechController with POST /api/speech/transcribe for audio
transcription and GET /api/speech/health for provider status.
Uses AudioValidationPipe for file upload validation and returns
results in standard { data: T } envelope.
Includes 10 unit tests covering transcribe with options, error
propagation, and all health status combinations.
Fixes#392
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add fallback-tier TTS provider using Piper via OpenedAI Speech for
ultra-lightweight CPU-only synthesis. Maps 6 standard OpenAI voice
names (alloy, echo, fable, onyx, nova, shimmer) to Piper voices.
Update factory to use the new PiperTtsProvider class, replacing the
inline stub. Includes 37 unit tests covering provider identity,
voice mapping, and voice listing.
Fixes#395
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create AudioValidationPipe for MIME type and file size validation,
TextValidationPipe for TTS text input validation, and DTOs for
transcribe/synthesize endpoints. Includes 36 unit tests.
Fixes#398
Add ChatterboxSynthesizeOptions interface with referenceAudio and
emotionExaggeration fields, and comprehensive unit tests (26 tests)
covering voice cloning, emotion control, clamping, graceful degradation,
and cross-language support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract KokoroTtsProvider from factory into its own module with:
- Full voice catalog of 54 built-in voices across 8 languages
- Voice metadata parsing from ID prefix (language, gender, accent)
- Exported constants for supported formats and speed range
- Comprehensive unit tests (48 tests)
- Fix lint/type errors in chatterbox provider (Prettier + unsafe cast)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix prettier formatting for Tooltip formatter props (single-line)
- Fix no-base-to-string by using typed props instead of Record<string, unknown>
- Fix restrict-template-expressions by wrapping number in String()
Refs #375
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
Add the BaseTTSProvider abstract class and TTS provider factory that were
part of the tiered TTS architecture but missed from the previous commit.
- BaseTTSProvider: abstract base with synthesize(), listVoices(), isHealthy()
- tts-provider.factory: creates Kokoro/Chatterbox/Piper providers from config
- 30 tests (22 base provider + 8 factory)
Refs #391
Add abstract BaseTTSProvider class that implements common OpenAI-compatible
TTS logic using the OpenAI SDK with configurable baseURL. Includes synthesize(),
listVoices(), and isHealthy() methods. Create TTS provider factory that
dynamically registers Kokoro (default), Chatterbox (premium), and Piper
(fallback) providers based on configuration. Update SpeechModule to use
the factory for TTS_PROVIDERS injection token.
Also fixes lint error in speaches-stt.provider.ts (Array<T> -> T[]).
30 tests added (22 base provider + 8 factory), all passing.
Fixes#391
- Add CHAT_PROVIDERS injection token for bridge-agnostic access
- Conditional loading based on env vars (DISCORD_BOT_TOKEN, MATRIX_ACCESS_TOKEN)
- Both bridges can run simultaneously
- No crash if neither bridge is configured
- Tests verify all configuration combinations
Refs #379
The mosaicstack-telemetry package lacks py.typed marker. Add type
ignore comment consistent with other import sites.
Refs #370
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>