10 Commits

Author SHA1 Message Date
chahinebrini
1a270739bc feat(coach): Lyra-LLM auf direkten Anthropic Haiku 4.5 + SOS-Fallback-Kette; ElevenLabs reaktiviert
- Chat (message.post.ts): neuer nativer anthropic-Branch in tryModel
  (api.anthropic.com/v1/messages, x-api-key, system top-level), führt die
  Fallback-Kette claude-haiku-4-5 → gemini-flash-lite → gemini-flash → gpt-4o-mini.
- SOS (sos-stream.get.ts): Dispatch-Refactor mit buildUpstream() + Kandidaten-
  Fallback-Kette (anthropic → gemini → openai). Behebt strukturell den Bug
  "SOS liefert nur Krisen-Text" (vorher single fetch ohne Fallback). Nativer
  Anthropic-Stream: Delta-Parser liest content_block_delta.delta.text.
- nitro.config.ts: anthropicApiKey deklariert (ANTHROPIC_API_KEY).
- plan-features.ts: Legend-Voice zurück auf ElevenLabs eleven_turbo_v2_5
  (Cartesia-Übergang nach Zahlungsproblem 2026-06-08 aufgehoben, neuer Key).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:41:59 +02:00
chahinebrini
0bff819b09 fix(tts): Legend übergangsweise auf Cartesia (ElevenLabs payment_issue 401)
ElevenLabs-Account hat ein Zahlungsproblem → Legend-TTS bekäme 502.
Leite Legend bis zur Klärung auf Cartesia sonic-3 (unlimited), damit
Legend-User Sprachausgabe behalten. Revert-Config steht im Kommentar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 03:04:17 +02:00
chahinebrini
a95e66560d feat(magic): Hard-Lock + Geräte-UX (Push, Realtime, Detail-Sheet, Offline-Removal)
Devices/Magic:
- Offline-Profil-Enroll deaktiviert (410) — Lock-PW würde im Klartext im
  Download landen; stationärer Schutz läuft jetzt nur über Rebreak Magic
- Mac-DNS-Template: ProhibitDisablement (Filter nicht abschaltbar)
- Push "Neues Gerät verbunden" an mobile Geräte bei neuer Bindung
- Realtime auf user_devices → Settings aktualisiert Magic-Bindings live
- Geräte-Detail-Sheet (Tap auf Gerät): Status, verbunden-seit, Schutz-Donut

Hard-Lock (server-gehaltenes Removal-PW, User sieht es nie):
- magic_removal_password generiert/gespeichert + in Profil injiziert (Lazy-Backfill)
- Reveal NUR bei Account-Löschung (user/delete) + Kündigung (stripe webhook),
  per Resend-Mail + in-Response
- Signing config-gated (inaktiv ohne Cert; Lock greift auch unsigniert)

Migrations: user_devices-Realtime-Publication + magic_removal_password-Spalten

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:26:25 +02:00
chahinebrini
5b57bea9c0 perf(mail): kill redundant 30min scan-cron + in-flight scan guard
Backend-Lag-Fix Phase 1 — entlastet die CPU-Dauerschleife im Mail-Stack:
- delete mail-scan-cron.ts: der 30-Min-Nitro-Cron scannte alle User parallel
  (Promise.allSettled) und war redundant zum IMAP-IDLE-Daemon (Single Source
  of Truth). Reine Dauerlast ohne Mehrwert.
- imap-idle: In-Flight-Guard (scanInFlight + coalescePending). triggerScan ist
  jetzt re-entry-safe — pro Connection max. 1 aktiver + 1 pending Scan statt
  bis zu 8 gestapelt pro 2-Min-NOOP-Tick. Gilt für NOOP + exists-Event.
- plan-features: Pro mailAgents 3->2 (+ Math.min-Hack in coach/message aufgeräumt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 10:38:06 +02:00
chahinebrini
f9d44a6754 feat(coach): mehrsprachiges TTS — locale durchreichen + Cartesia Sonic-3
TTS-Sprache war provider-übergreifend hart auf "de" verdrahtet, locale aus dem
Request wurde ignoriert → arabischer Text wurde deutsch-phonetisch gesprochen.

- locale aus Body auslesen → Basis-Sprachcode an alle Provider
- Pro: Cartesia sonic-2 → sonic-3 (sonic-2 kann kein Arabisch; sonic-3 = 42 Sprachen)
- Legend: ElevenLabs language_code gesetzt (turbo_v2_5 multilingual, ar dabei)
- Google-Fallback: BCP-47-Map (ar→ar-XA etc.), de-Voice nur noch für de

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 23:57:12 +02:00
chahinebrini
704958320b refactor(domains): gemeinsamer 10/20-Slot-Pool, Free-Tier entfernt
Custom-Domain-Slots sind jetzt EIN gemeinsamer Pool für web + mail
(Pro 10 / Legend 20) statt getrennter web/mail-Buckets. Free-Tier ist
entfallen — PLAN_LIMITS hat nur noch pro + legend, getPlanLimits
defaultet auf pro.

Backend:
- plan-features: customDomains ist eine Zahl (CustomDomainLimits weg)
- index.post: Slot-Check gegen Gesamt-Count, Fehler einheitlich LIMIT_REACHED
- index.get: liefert { items, count, limit }
- change-preview + coach/message an die neue Form angepasst

Frontend:
- useCustomDomains: count/limit (Zahlen) statt countsByType/limits
- AddDomainSheet: ein generischer Limit-Hinweis (error_limit_reached)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:40:28 +02:00
chahinebrini
f2b81eef54 feat(backend/plan): separate web/mail slot pools + display-name submit lock
plan-features.customDomains is now { web, mail } per plan instead of a
single number. Free 5+5, Pro 5+5, Legend 10+10 — the user explicitly
chose separate pools so users don't have to trade a website slot for a
mail-pattern slot or vice versa.

- countActiveCustomDomainsSplit(userId) groupBy type → { web, mail }
  (mail aggregates mail_domain + mail_display_name). Old single-count
  function stays as a deprecated alias for any caller still on it.
- POST /api/custom-domains: body-compat accepts both { pattern, kind }
  (current frontend) and { domain, type } (legacy / direct). kind='mail'
  is split into mail_domain vs mail_display_name server-side based on
  whether the pattern looks like a domain. Slot check is per-bucket;
  errors are WEB_LIMIT_REACHED / MAIL_LIMIT_REACHED so the UI can show
  the right limit-reached message per tab.
- GET /api/custom-domains: response shape extended to
  { items, counts: { web, mail }, limits: { web, mail } } so the
  frontend can drive the per-tab counter without client-side estimation.
- POST /api/custom-domains/:id/submit: hard-blocks mail_display_name
  with 400 DISPLAY_NAME_NOT_SUBMITTABLE. Display-name submission to the
  global blocklist is deferred to v1.1 — would require a schema split
  on BlocklistDomain that's risky pre-TestFlight. mail_domain still
  flows through the community-vote pipeline like web entries.
- auth/me.get.ts, plan/change-preview.get.ts, coach/message.post.ts
  updated for the new shape (Lyra prompts untouched, only template
  variables split web vs mail counts).

24 vitest cases in backend/tests/custom-domains/plan-limits.test.ts
cover the new shape, body compat, bucket logic, and the submit guard;
216/216 total backend tests pass.
2026-05-16 02:03:26 +02:00
chahinebrini
335945fe2c feat(tier): plan limits Rev.2 + downgrade reconciliation + change-preview (Phase 2 backend)
- plan-features.ts: globalBlocklist 'curated'|'full' (curated = 30-domain stub,
  TODO real ~1-2k HaGeZi subset); maxAppDevices vs maxProtectedDevices split
  (legend maxProtectedDevices: 2); mail 1/3/Infinity
- limit-enforcement structured errors on mail/connect, custom-domains/add, devices/enroll
  ({ error:'plan_limit', resource, current, limit }); approved-own-submissions already
  excluded from custom-domain count (slot frees on approval)
- server/utils/downgrade-reconciliation.ts: founding-member exemption; re-upgrade
  reactivates paused mail + degraded devices; downgrade pauses newest-N mail accounts
  (isActive=false, pausedAt, pausedReason; pre-pause sets nextScanAt=now for a final
  sweep — real direct IMAP scan is TODO/stub); degrades excess device profiles
  (status='degraded', degradedAt); free → globalBlocklistGraceUntil = now+14d;
  custom domains grandfathered
- set-plan.post.ts + stripe/webhook.post.ts: run reconciliation on plan change;
  set-plan accepts { foundingMember } for testing
- GET /api/plan/change-preview?to=<plan>: gains/keeps/changes per resource (8 axes),
  founding-member → direction 'same'
- me.get.ts: + foundingMember, globalBlocklistGraceUntil, planLimits block
- blocklist + mail-scan honour globalBlocklistGraceUntil (grace → treat as 'full')
- db: countMailConnections/getMailConnections exclude paused; getAllMailConnections;
  getDeviceBlocklistMode (active|grace|passthrough|revoked)
- migration 20260511_tier_system_phase2 (profiles.founding_member +
  global_blocklist_grace_until; mail_connections.paused_at/paused_reason;
  protected_devices.degraded_at). prisma generate + build:backend clean.

TODOs (separate tickets): founding-member auto-counter on signup; real direct IMAP
final-scan (not just nextScanAt nudge); real curated blocklist data + wiring the
stub into the blocklist response for free users.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:23:02 +02:00
chahinebrini
b40b8465b9 feat(lyra,voice): founder-story + voice-tier-mapping + quota system
Two features in one push (both backend, deploy together):

LYRA FOUNDER-STORY (per strategist Option C — mixed/medium-detail):
- COACH_CASUAL_SYSTEM_PROMPT: GRÜNDER-STORY sub-block
  - Sharing-rules: ALWAYS on direct ask, RARELY proactive (only on
    explicit isolation expressions "niemand versteht das"), NEVER in
    SOS-mode, NEVER first-3-msgs, NEVER if user appears minor
  - Detail-level: "aus persönlicher Erfahrung mit Spielsucht in seiner
    Familie" — KEINE Namen, Verwandtschaftsgrade, Verlust-Details
  - Post-share-pivot: "...aber jetzt zu dir: was ist gerade los?"
- COACH_SYSTEM_PROMPT (SOS): SOS-MODE LOCK — hard-Verbot Gründer-Story
  zu erwähnen, auch bei direct-ask. Re-trigger-Risk zu hoch.
- DSGVO: brother bleibt komplett anonymisiert. Hans-Müller-DSB-review für
  verbal-consent-doc empfohlen.

VOICE TIER-MAPPING (per user-decision: voice für ALLE tiers):
- New plan-features.voice config: provider + model + voiceId + dailyQuotaSeconds
- Tier-mapping:
  - Free  → Google TTS Neural2-F (de-DE), 60s/day,  ~$4/1M chars
  - Pro   → Cartesia Sonic-2,            300s/day,  ~$4/1M chars + ~75ms TTFT
  - Legend → ElevenLabs Turbo v2.5,      unlimited, ~$30/1M chars
- New backend/server/db/voiceQuota.ts:
  - getRemainingVoiceQuota(userId, plan)
  - consumeVoiceQuota(userId, seconds)
  - estimateAudioSeconds(text)
- speak.post.ts komplett umgeschrieben als plan-aware dispatcher
- 14 tests passing (partial-consume, exhausted, day-rollover, edge-cases)
- Schema-migration 20260509_voice_quota:
  ADD voice_seconds_used_today, voice_quota_reset_at to profiles
  (auto-deploy via pipeline)

Pending Frontend (separate task):
- Voice-quota-UI in Settings/Profile (remaining seconds + upgrade-prompt
  bei 429 quota_exceeded)

⚠️ Schema-migration auto-deploy via b38bf17 detection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:28:36 +02:00
RaynisDev
b58588cf3c initial commit: rebreak-monorepo (RN app + standalone Nitro backend) 2026-05-06 07:13:43 +02:00