Commit History
-
fix(notifications): skip permission request if already denied by browser
CognxSafeTrack84fb6f6 -
chore: trigger Netlify rebuild with correct VITE_API_URL
CognxSafeTrack255078f -
docs: add developer audit guide (2026-05-15)
CognxSafeTrack5e23bc3 -
fix(crm): normalize Excel column names before bulk contact upload
CognxSafeTracksheet_to_json returns raw column headers (Téléphone, Mobile, Phone…) but the API expects exactly `phoneNumber`. Added normalizeContacts() that matches common phone/name column variants case-insensitively and maps them to the schema — remaining columns go into `attributes`. Also: show a clear error if no phone column is found, and fix the `Error: [object Object]` message that was hiding the real API error.3d1299e -
fix(admin): replace vague Meta hints with direct clickable links in Direct Setup modal
CognxSafeTrack- WABA ID → business.facebook.com/settings/whatsapp-business-accounts - Meta Business ID → business.facebook.com/settings/info - Token → business.facebook.com/settings/system-users (new hint added) - Updated i18n labels in en/fr/pt295c327 -
feat(db): remove unique constraint on Organization.wabaId
CognxSafeTrackAllow multiple organizations to operate under the same WABA (one WhatsApp Business Account, multiple phone numbers / orgs). Migration applied directly on Neon — shadow DB blocked by pgvector.348fb8a -
feat(admin): add Direct Setup button for super admins on orgs without phone number
CognxSafeTrack- Add "Direct Setup" button (super admin only) on /clients for orgs without a WhatsApp number - Previously the setup modal was only reachable via the Reconfigure button (orgs with a phone) - Add i18n keys: clients.actions.direct_setup (en/fr/pt) - Add scratch scripts for org configuration and DB queries - Add doc: configurer-token-nouvelle-org.md — how to configure a Meta token on a new orga3df350 -
docs: add HuggingFace → Railway migration plan
CognxSafeTrack17a3e29 -
docs: add post-mortems for Meta API deprecation, SSE 401 and file import bugs
CognxSafeTrackb8d93e2 -
fix(clients): resolve file import dialog error and SSE 401
CognxSafeTrackFile import (Bug 1): - CrmAIAssistant had its own <input type="file"> that called onFileUpload onChange, which then called .click() on #crm-file-upload — a second programmatic file dialog triggered from an onChange handler. Chrome 126+ blocks this as it lacks a direct "user activation". - Fix: replace the embedded <input> with <label htmlFor="crm-file-upload"> so the click routes directly to FileImporter's input. Remove the now-unused onFileUpload prop entirely. SSE 401 (Bug 2): - EventSource (browser API) cannot send custom headers — the JWT token never reached the server, causing a 401 on every SSE connection. - Fix: pass the token as ?token= query param in the EventSource URL (MainLayout + CrmConversationalDashboard). - Backend: verifyJwt now injects the query param token into the Authorization header before calling jwtVerify(), so no route changes are needed.87dcd87 -
feat(observability): log Meta Graph API version at startup
CognxSafeTrackBoth API and Worker now emit a ✅ info log at boot showing the active META_GRAPH_API_VERSION, or a ⚠️ warn if the version is below v21.0 (which signals a likely-deprecated version). Visible directly in Railway logs without digging into the code.d66ae60 -
fix: upgrade Meta Graph API from deprecated v18/v19 to v22.0
CognxSafeTrackAll hardcoded v18.0 and v19.0 references replaced with a centralized META_GRAPH_API_VERSION env var (default: v22.0). v19.0 was deprecated in February 2026 causing messaging_limit_tier to silently disappear from the /clients daily-limit card. Also improves error visibility: the catch block in fetchMetaStatus now logs the full error instead of swallowing it silently, and a warn is emitted when messaging_limit_tier is absent even on a successful response (signals a missing BUSINESS_MANAGEMENT permission).b25d16e -
feat(i18n): complete super-admin i18n — all 11 pages fully translated
CognxSafeTrackAll 11 super-admin pages now use useTranslation for every visible string. Adds rel_today/rel_yesterday/rel_days_ago keys for WhatsAppProfiles relativeDate, tpl_cat_*_desc keys for template category descriptions, and fixes the SuperAdminLayout NAV_ITEMS destructuring (labelKey vs label).b92ea37 -
feat(i18n): complete admin app internationalization across all pages
CognxSafeTrackReplace all hardcoded French strings with t() calls on 14 pages and sync 130+ new keys across fr/en/pt locale files. Language switching now works everywhere in the admin interface. Pages covered: TrackDaysPage, TrackFormPage, TrackListPage, UserListPage, KnowledgeBasePage, SettingsPage, AnalyticsPage, AIAgentSetup, OnboardingWizard, ResetPasswordPage, ClientsManagementView, ContactsPage, TemplatesPage, TrainingLab.d80fec4
-
fix: restore Limite Quotidienne and complete admin i18n
CognxSafeTrackFix messaging_limit_tier disappearing in /clients page: - fetchMetaStatus was querying WhatsAppPhoneNumber separately, which is tenant-filtered by Prisma extension — returns null when called from another org's JWT context (super-admin viewing all orgs) - Fix: include phoneNumbers in the Organization query (excluded from tenant filtering) and use org.phoneNumbers[0].id directly i18n — pages fully translated (fr/en/pt): - LoginPage: org ID, email, password, subtitles, button, footer - ResetPasswordPage: all 4 states (request/sent/new/success) - BillingPage: 62 keys — wallet, transactions, chart, AI assistant, credit packs, QUICK_QUESTIONS, TX_LABELS, FEATURE_LABELS - ClientsManagementView (/clients): 20 key groups — status badges, WABA labels, tier/quality info, all modals (billing, credits, create org, personality, WA setup), validation errors, toasts Fix TemplatesPage status badges showing raw key strings: - Add 5 missing keys to all 3 locale files: status_approved, sta…4f90920 -
fix(audit): resolve all remaining technical debt issues
CognxSafeTrackC2 - analytics SQL injection: UNION detection, ALLOWED_TABLES allowlist, remove SQL from error responses to prevent schema leakage H1/H2 - override-feedback: move organizationId validation to top of handler before any DB mutation; scope enrollment.findFirst to org H6 - UserListPage: add pagination controls (Précédent/Suivant, 20 per page) L4 - TrackDaysPage: await load() before closing editor to prevent stale data race L5 - admin.ts: replace getOrganizationId() with req.organizationId at 3 call sites, remove now-unused import M1 - EntityResolver: replace any[] types with proper Prisma payload types (ResolvedUser/Contact/Organization/Enrollment with exact include shapes) L8 - AI command fuzzy org match: return pending_confirmation when org found by partial name match, inject orgId for unambiguous confirmation retrya888244 -
fix(security): resolve critical and high technical debt from audit
CognxSafeTrackCRITICAL: - Exclude passwordHash from enrollment list response (admin.ts) - Add sanitizeOrg() to strip API keys/tokens from all org GET responses - Fix CORS wildcard on SSE stream; use request origin instead - Restrict super-admin routes to SUPER_ADMIN role only (was also allowing ADMIN) - Replace all Zod .parse() with .safeParse() → proper 400s instead of 500s (ai.ts, admin.ts) HIGH: - Increase ExerciseHandler Redis dedup lock from 30s to 300s - Add take:500 to unbounded trainingData.findMany (OOM risk) - Add take limits to message/contact findMany queries - Add Redis lock in EnrollHandler to prevent duplicate enrollments (TOCTOU) - Wrap override-feedback in try/catch; return 404 on P2025 - Use atomic increment for wallet credits (was TOCTOU race condition) - Scope messages/send to requesting org (was cross-org accessible) MEDIUM: - decryptSecrets returns new object instead of mutating argument in-place - GeminiProvider.generateStructuredData now validates response with Zod schema -…a8e18d6 -
fix(super-admin): guard null wabaId, org.id, amount in profiles/templates/billing/AI pages
CognxSafeTrackb5dcbdb -
chore(scripts): clean up copy-wa-config utility script
CognxSafeTrack338c360 -
feat(super-admin): WhatsApp OTP registration and template creation
CognxSafeTrack- WhatsApp service: add registerPhoneNumber() and verifyPhoneNumber() methods - Super-admin routes: POST /whatsapp/numbers/register and /verify for Meta OTP flow - Super-admin routes: POST /whatsapp/templates to submit templates to Meta for approval - WhatsAppNumbers page: 2-step modal (orgId + phoneNumberId + PIN → OTP code) - WhatsAppTemplates page: full creation modal with category cards, char counters, live preview bubble170eb5d -
feat(super-admin): org delete, user password reset, complete CRUD coverage
CognxSafeTrackBackend: - DELETE /v1/super-admin/organizations/:id — soft-delete via subscriptionStatus='DELETED' + isHardStopped=true - POST /v1/super-admin/users/:id/reset-password — generates 1h JWT reset token, emails link via scheduleEmail (same flow as /forgot-password) Frontend: - OrganizationsManager: red trash icon per row calls DELETE; confirmation dialog before destructive action; suspend icon color changed to amber - UsersManager: key icon per row calls reset-password endpoint; guards against users with no email addressb8629ec -
feat: complete super-admin interface + enforce Day 11 photo requirement
CognxSafeTrackSuper-admin (3 missing pages + 3 new backend routes): - AuditLogs page: date-range / action filter, paginated table with inline JSON detail expansion - WhatsAppTemplates page: org directory with lazy per-org template fetch; category/status badges; expandable template panel per row - WhatsAppProfiles page: card grid showing org branding (logo, color, WABA ID, token age); inline edit form with live logo preview and color picker; PATCH /v1/super-admin/whatsapp/profiles/:id - Backend: GET /audit-logs, GET/PATCH /whatsapp/profiles, GET /whatsapp/templates added to super-admin.ts - SuperAdminLayout: 10 nav items (added Templates WA, Profils WA, Audit Logs); SuperAdminRouter wired to all new routes Day 11 photo enforcement (tasks/todo.md item resolved): - FeedbackHandler: if currentDay === 11 and no imageUrl, send multilingual photo prompt and return early — exercise cannot be validated without a team member photo8f2286f -
feat: CSV export, monthly report, and quota alert plan filter
CognxSafeTrack- BillingPage: add "CSV" download button that exports all wallet transactions (date, type, description, amount, balance) as UTF-8 CSV - scheduler.ts: add startMonthlyReportScheduler() — cron on 1st of month 07:30 UTC; sends per-org recap of AI calls, WhatsApp messages, top-3 features, credits recharged/spent, and current balance - index.ts: register startMonthlyReportScheduler() alongside existing crons - usage-tracker.ts: quota alert (85% threshold) now only fires for GROWTH, SCALE, and ENTERPRISE plans; STARTER alerts were high-noise / low-valuea24fb7f -
chore: add api/worker docker-compose services and route validation tests
CognxSafeTrack- docker-compose.yml: add `api` (port 7860) and `whatsapp-worker` (port 8082) services with healthchecks on postgres/redis, SERVICES env var to select PM2 process, and RAILWAY_INTERNAL_URL wired between containers - test/routes.test.ts: 31 tests covering campaign schema validation (send, broadcast, generate), analytics SQL injection pattern guard, and auth route unauthenticated access — all pure logic, no DB required862b2b1 -
feat(settings): expose branding logoUrl and primaryColor fields
CognxSafeTrackAdds a Branding section to SettingsPage that reads/writes org.brandingData.logoUrl and org.brandingData.primaryColor. Includes a live logo preview and a color picker + hex input. test(security): add 15 SQL injection and auth tests Pure unit checks for the DANGEROUS_PATTERNS set used in the text-to-SQL guard (UNION, DELETE, DROP, comments, stacked queries). Integration auth checks for super-admin JWT enforcement.ec1b111 -
fix(super-admin): move routes to JWT-only scope to bypass tenant extension
CognxSafeTrackSuper-admin routes need cross-org Prisma access. The previous registration inside the tenant-guarded scope called runWithTenant('xamle-admin-org'), which caused the Prisma extension to silently filter all User, Message, WalletTransaction and other queries to a single org. Moving to a separate scope with JWT-only auth (no runWithTenant) means getOrganizationId() returns null inside these handlers, so the tenant extension skips filtering — enabling true cross-org stats and queries.ea8815c -
feat: add XAMLÉ Platform Admin super-admin interface
CognxSafeTrackWhen selectedOrgId === 'xamle-admin-org', the admin switches to a full platform management dashboard instead of the normal org layout. Backend (apps/api): - New /v1/super-admin/* routes (SUPER_ADMIN/ADMIN role required): platform stats, organizations CRUD + suspend + impersonate, cross-org users + role change, WhatsApp numbers, billing transactions + manual credits, monitoring health, AI agentic command - Fixed SQL injection in analytics text-to-SQL (UNION/DROP/comment blocking) - Removed JWT secret fallback — crashes on startup if JWT_SECRET is missing - Added ORG_ADMIN role guard to all /v1/admin/* routes Frontend (apps/admin): - SuperAdminRouter: nested routes under /platform/* - SuperAdminLayout: dark sidebar (slate-950/violet), 7 nav items - PlatformDashboard: 6 KPI cards, system health, active alerts - OrganizationsManager: searchable table, suspend/edit drawer, create modal - UsersManager: cross-org table with inline role-change - WhatsAppNumbers: all registered numbers …6282d86 -
feat(whatsapp-setup): make token optional when org already has one stored
CognxSafeTrackIf an org already has a systemUserToken, the /whatsapp-setup route now reuses it automatically. Admins can reconfigure WABA ID + Phone Number ID without re-entering the token — useful for adding a new phone number to an existing connected account.de46926 -
feat(settings): add WhatsApp reconfiguration form to SettingsPage
CognxSafeTrackBusiness owners can now connect or reconnect their WhatsApp account directly from Settings without going through the onboarding wizard. Form accepts WABA ID, System User Token, Phone Number ID, and phone number.ec8f103 -
fix(crm-inbox): unwrap paginated messages response to restore conversation list
CognxSafeTrackThe GET /messages endpoint was changed to return {data, total, limit, skip} but the frontend still consumed it as an array, causing setInboxMessages to receive an object and the conversation list to always show empty.0da3feb -
fix(crm-agent): replace generateText+JSON.parse with generateStructuredData for intent detection
CognxSafeTrackThe CRM command agent was always falling back to GENERAL_CHAT and showing the menu again because LLMs wrap JSON in markdown fences, breaking JSON.parse. Switch to generateStructuredData with a Zod schema which guarantees structured output regardless of LLM quirks. Also exposes generateStructuredData as a public method on AIService.3b3d6cd -
chore: sync pnpm lockfile after stripe removal
CognxSafeTrack +1: Claude Sonnet 4.6Regenerated pnpm-lock.yaml to remove stale stripe specifier that was causing frozen-lockfile failures on Hugging Face CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>fb47dd6 -
fix(audit): resolve remaining TypeScript any types, string enums, and Json validation
CognxSafeTrack +1: Claude Sonnet 4.6TypeScript types (ai-sdk): - Define AIServicePrismaClient structural interface — replaces prisma: any - Define TenantSecrets interface — replaces getTenantSecrets: () => Promise<any> - Define BusinessProfileContext interface — replaces businessProfile?: any in 3 methods - Define BusinessProfileExtraction interface — extractBusinessProfile now fully typed - Define ContactData interface — replaces contact: any in generateCrmCampaign - exerciseCriteria? typed as Record<string, unknown> instead of any - searchResults now typed as SearchResult[] instead of any[] DB enums: - CampaignHistory.status: String → CampaignStatus enum (SENT | DELIVERED | READ | FAILED) - AnalyticsLog.eventType: String → AnalyticsEventType enum (CLICK | READ | RESPONSE) - Migration 20260514000001 applied to production DB Zod validation: - TrackDaySchema: add exerciseCriteria (z.record) and badges (z.array(z.string)) - ai.ts /crm/generate-campaign: enforce contact.phoneNumber (required) via Zod Runtime safety: - wh…2a3e2a1 -
fix(audit): resolve remaining minor technical debt issues
CognxSafeTrack +1: Claude Sonnet 4.6AI/BYOK: - generateText now respects tenant BYOK keys via getProvidersForTenant() (was using global registry) - generateText now applies exponential backoff retry via AIService.withRetry() Backend: - notifications.ts: return 503 instead of 200+undefined when VAPID_PUBLIC_KEY absent - BillingPage: support WA number now reads from VITE_SUPPORT_WA_NUMBER env var (was hardcoded fake number) - KnowledgeBasePage: show toast.error on delete failure (was silently swallowed) DB: - Add @@index([organizationId, exerciseStatus]) on UserProgress for live-feed query performance - Add @@index([createdAt]) on AuditLog for time-range admin queries - Migration 20260513000005 applied to production DB i18n: - Add knowledge.delete_error translation key to all 4 locales (fr, en, es, pt) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>d74c1b3
-
fix(audit): resolve 15 critical and major technical debt issues
CognxSafeTrack +1: Claude Sonnet 4.6Security: - campaigns.ts: prevent cross-tenant write by using req.organizationId instead of URL param - billing.ts: restrict org mode change to SUPER_ADMIN only (was incorrectly allowing ADMIN) - CommandHandler: guard SEED command with NODE_ENV !== 'production' - search.ts: return [] instead of fabricated mock data when SERPER_API_KEY absent - scheduler: filter out hard-stopped organizations from nudge and lesson delivery - payments.ts: HMAC signature verification for Wave and Orange Money webhooks + fix track.priceAmount Data integrity: - schema: TrackDay.dayNumber Float → Int, Enrollment.currentDay Float → Int - schema: add @@unique([userId, trackId]) to Enrollment to prevent race condition double-enrollment - Migration 20260513000004 applied to production DB Performance: - admin.ts /live-feed: fix N+1 queries with batch findMany + in-memory map - organizations.ts /:id/messages: add pagination (was unbounded, OOM risk) - crm-service.ts listMessages: add skip/limit/total support Co…97f541f -
ci: add GitHub Actions workflow — typecheck, tests, build
CognxSafeTrack +1: Claude Sonnet 4.6Three jobs on push/PR to main: - typecheck: tsc --noEmit on api, whatsapp-worker, admin (after Prisma generate + ai-sdk build) - test: vitest run (excluding AI regression) against real Postgres/pgvector + Redis service containers, runs migrate:deploy before tests - build: pnpm turbo run build gated on typecheck passing Concurrency group cancels in-progress runs on new push to same branch. AI regression tests (test/regression/) excluded — require real API keys, run manually. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>56d35ea -
fix(jour11): real Gemini vision for team member extraction + correct mediaType
CognxSafeTrack +1: Claude Sonnet 4.6- ai-sdk: add extractTeamMemberFromImage() — uses callWithFailover with VISION capability (Gemini base64 inlineData), structured schema {name, role, bio} - FeedbackHandler: replace generateText() (text-only) with extractTeamMemberFromImage() so Day 11 photos are actually analyzed visually, not just via URL in text prompt - MediaHandler: fix hardcoded mediaType AUDIO → IMAGE for image mimeTypes; remove duplicate isImage declaration in spinner block Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9d56788 -
feat: gold standards tests (15 days + STT + vision) & botName editable in AI setup
CognxSafeTrack +1: Claude Sonnet 4.6- ai-gold-standards: 40 test cases covering all 15 lesson days, STT smoke, vision (day 11 imageUrl), CRM broadcast/conversation, curriculum (FR+EN), pitch deck EN, business profile extraction days 1-6 with quality assertions - AIAgentSetup: botName field now editable (was hardcoded 'Agent IA'); loaded from personality API on mount, saved with PATCH; falls back to 'Agent IA' if blank - i18n: add bot_name_label/placeholder/hint keys to FR, EN, ES, PT locales Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>1c602ea -
chore: remove Stripe — replaced by Orange Money / Wave payment gateway
CognxSafeTrack +1: Claude Sonnet 4.6Delete stripe.ts route file, remove import and /v1/stripe registration from app.ts, drop stripe npm dependency, remove deprecated stripeWebhookRoute stub from payments.ts, and remove stripeCustomerId from admin Organization interface. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>641e72b -
feat: complete agentic audit roadmap — retry, KB generate, real costs, traceId, rate-limit
CognxSafeTrack +1: Claude Sonnet 4.6- ai-sdk: exponential backoff retry (3 attempts, 1s→2s) in callWithFailover; skips 4xx and QuotaExceededError - api/whatsapp: traceId (crypto.randomUUID) propagated to worker bridge via x-trace-id header - api/whatsapp: route-level rate limit 500/min on POST /webhook, returns 200 on rejection - api/organizations: POST /:id/kb/generate — GPT-4o-mini JSON mode generates FAQ pairs, embeds via IndexingService - api/indexing-service: new service batching OpenAI text-embedding-3-small + pgvector INSERT - admin/AnalyticsPage: real cost from UsageEvent aggregate + byFeature breakdown table - admin/KnowledgeBasePage: KB auto-generation panel (description textarea, generate button, FAQ preview) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b438786 -
fix: security hardening, real AI costs, traceId propagation & audit logging
CognxSafeTrack +1: Claude Sonnet 4.6- S1: enforceOrgIsolation — organizationId always from JWT for non-SUPER_ADMIN (header ignored) - S4: auditService.log() on ORG_MODE_CHANGED, ORG_PERSONALITY_UPDATED, ORG_BYOK_KEYS_UPDATED - O1: traceId (UUID) generated at webhook, forwarded via x-trace-id header, injected into BullMQ job data, threaded through whatsapp-logic handler chain - D3: /v1/analytics/usage now aggregates real UsageEvent.costUsd + tokens (tokensIn/Out) with per-feature breakdown instead of token estimates - D4: BrandingDataSchema added to shared-types — brandingData strictly typed (logoUrl, primaryColor, orgName) instead of z.any() - A6: InboundHandler fetches user.language in parallel with org token, passes it to Whisper - A2: sendLessonDay() caches personalized lesson text in Redis 6h by (trackDayId, activity, language) - P1: sendLessonDay() parallelized — user+trackDay batched in one include, userProgress fetched in parallel - D2: ExerciseHandler Redis 30s dedup lock prevents double exercise submission Co-A…87afdf1 -
feat: agentic platform — text-to-sql, pedagogy advisor, security hardening & performance
CognxSafeTrack +1: Claude Sonnet 4.6- Analytics: POST /v1/analytics/query — natural language → GPT-4o-mini → SELECT → results (Text-to-SQL) - Analytics UI: example chips, SQL results table, 'Voir SQL' toggle - Billing: role guard on change_organization_mode (ADMIN+ only) - Nav: /ai-setup accessible to all org members (not just super admin) - Scheduler: startPedagogyAdvisor() — Wednesday 08:30 UTC cron, at-risk nudges (5 languages) + email report - S1 security: organizationId always from JWT for non-super-admin (never from header) - A6: InboundHandler passes user.language to Whisper transcription (Wolof hint: 'fr') - A2: Redis 6h cache for personalized lessons keyed by (trackDayId, activity, language) - P1: sendLessonDay() parallelized from 4 sequential DB queries to 2 (user+trackDay batch, userProgress parallel) - D2: Redis 30s dedup lock on exercise submission to prevent double-submit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>5b8761d -
feat(jour11+schema): Jour 11 photoUrl on TeamMember + audit doc updates
CognxSafeTrack +1: Claude Sonnet 4.6- TeamMember.photoUrl: new optional field stores the original WhatsApp image URL alongside the AI-extracted name/role/bio - extractAndSaveTeamMember(): now persists photoUrl field - Migration 20260513000003_add_team_member_photo_url - Audit doc: mark Semaine 3-4 fully complete, add Stripe + Jour 11 items Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>61fd7f5 -
fix(admin): migrate raw fetch() to api client in ClientsManagementView
CognxSafeTrack +1: Claude Sonnet 4.6Removes the last manual fetch() call outside lib/api.ts — billing/admin/allocate now goes through api.post() which injects Authorization and Content-Type automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>60c3938 -
feat: Claude Sonnet 4.6 provider + Stripe billing automation
CognxSafeTrack +1: Claude Sonnet 4.6AI: - packages/ai-sdk: ClaudeProvider implements LLMProvider via tool_use structured outputs + zod-to-json-schema - Claude registered at priority 120 (TEXT only), supersedes Gemini for feedback/lessons/chat — -40% cost, better French - ANTHROPIC_API_KEY added to API and worker env validation Stripe: - apps/api/src/routes/stripe.ts: POST /v1/stripe/webhook handles checkout.session.completed, subscription.updated/deleted, invoice.payment_failed/succeeded - Schema: stripeCustomerId + stripeSubscriptionId added to Organization - tenant.ts: 402 gate for isHardStopped orgs (x-api-key calls always pass) - ClientsManagementView: SUSPENDU badge on hard-stopped orgs - STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET added to config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4a0c3ba -
feat(crm-ux): tag management, unread badge, handoff release UI
CognxSafeTrack +1: Claude Sonnet 4.6- ContactsPage: tag filter chips, per-contact tag editing with inline input, add/remove tags via PATCH API, colSpan fix for new Tags column - MainLayout: SSE-powered unread message badge on conversations nav item, resets on navigation - UserListPage: soft-delete button, handoff status fetch + "Libérer l'IA" button in conversation modal - admin routes: DELETE /users/:id (soft-delete), GET/DELETE /users/:id/handoff endpoints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>42a2598 -
fix(org-resolve): support legacy non-UUID custom IDs (e.g. default-org-id) + analytics orgId header
CognxSafeTrack +1: Claude Sonnet 4.6- resolveOrgId now uses findFirst with OR [slug, id] so literal custom IDs like "default-org-id" (the seed org XAMLÉ Global) are resolved correctly instead of returning 404 - ContactsPage.tsx: pass selectedOrgId to analytics/usage call — was missing the x-organization-id header, causing systematic 400 on the contacts page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2cc44c6 -
feat(agentic-week2-4): 5 major features — AI content generator, handoff, tags, soft-delete, SSE
CognxSafeTrack +1: Claude Sonnet 4.6## Agent Créateur de Contenu (Semaine 3) - AIService.generateCurriculum() — structured GPT-4o prompt generates full Track + TrackDays - POST /v1/organizations/:id/content/ai-generate — persists curriculum directly to DB - TrackListPage: "Générer avec IA" button + modal (description, numDays, language, audience) - Auto-redirects to the generated program days after creation ## Human Handoff Detection (Semaine 4) - AIAgentHandler: detects 15 multilingual handoff keywords (FR/EN/ES) - Sets Redis flag handoff:{userId}:{orgId} (24h) — pauses AI responses - Emails org admin with contact info + link to admin UI - Handles subsequent messages while in handoff mode gracefully ## Contact Segmentation by Tags (Semaine 2) - Contact.tags String[] added to Prisma schema (migration + db:push applied) - GET /contacts/tags — list all unique tags in org - PATCH /contacts/:id/tags — set tags on a contact - GET /contacts?tags=tag1,tag2 — filter contacts by tags (hasSome) ## SSE Real-time Updates (Semaine…99f6bca -
fix(conversations): resolve /conversations blank page + slug→UUID KB routes + WABA banner
CognxSafeTrack +1: Claude Sonnet 4.6- Add missing /conversations route to App.tsx (was nav-linked but unregistered) - Apply resolveOrgId() to all 5 KB routes so slug-based org IDs (e.g. xamle-admin-org) no longer cause 404/400 - TemplatesPage: detect WABA-not-configured 400 and show amber banner instead of silently failing - KnowledgeBasePage: show toast on index-kb 400 (no KB URL) with actionable message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9f72dd7 -
feat(agentic-week1): conversational memory, RAG threshold, wallet alerts, weekly reports, campaign scheduling
CognxSafeTrack +1: Claude Sonnet 4.6- AIAgentHandler: Redis sliding window (20 entries, TTL 24h) for conversation history injected into system prompt — AI_AGENT now remembers context - IndexingService.searchRelevantContext: cosine threshold 0.70 — returns '' if no chunk is relevant so agent responds honestly instead of hallucinating - add-hnsw-index.ts: one-shot HNSW index script (m=16, ef=64) for ~10x faster pgvector cosine search on large knowledge bases - scheduler.ts: hourly wallet alert (email via Brevo if < 3 days runway, 6h Redis suppression) + weekly report every Monday 07:00 UTC with trend vs previous week and color-coded wallet status - queue.ts + campaigns.ts: sendAt ISO-8601 parameter on broadcast/campaign routes — BullMQ delay option schedules jobs natively, no cron needed - docs/agentic/: roadmap updated with Semaine 1 marked complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>98240fd