Commit History
-
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
-
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(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 -
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 -
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 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 -
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 -
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 -
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 -
refactor(worker): Sprint 2 — dead code removal and queue consolidation
CognxSafeTrack +1: Claude Sonnet 4.6H1: Delete credit-guard.ts (worker) and credit-cache.ts (api) — both fully superseded by wallet.ts/requireCredits.ts since the wallet migration. H6: Introduce lib/queues.ts with shared whatsappQueue and notificationQueue instances. Replace all 10 inline `new Queue(...)` calls across wallet.ts, usage-tracker.ts, whatsapp-logic.ts, scheduler.ts, and 4 handlers. Removes 10 redundant Redis connections; drops `q.close()` calls on ephemeral instances that were killing the shared connection. Shutdown handler now explicitly closes both shared queues. Scratch: Move apps/api/scratch/ one-shot DB scripts to scripts/db-ops/ so they don't ship in the production API bundle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>37fb9ce -
feat(billing): implement full wallet/ledger system with hard-stop enforcement
CognxSafeTrack +1: Claude Sonnet 4.6- Add walletBalance + isHardStopped to Organization schema; WalletTransaction ledger model with full audit trail - wallet.ts (worker): debitWallet (atomic $transaction), creditWallet (with AuditLog), checkWalletBalance (Redis 30s cache), alert queuing (low/critical/exhausted/restored) - wallet.ts (api): creditWallet for admin top-up with cache invalidation - usage-tracker.ts: debitWallet integrated into trackAiUsage, trackAudioUsage, trackWhatsAppSent; BYOK orgs skipped - BroadcastHandler + CampaignHandler: bulk debit after send (BROADCAST_PER_USER per recipient) - requireCredits.ts: migrated from aiCreditsUsed/Limit to walletBalance/isHardStopped; shares Redis cache key with worker - whatsapp-logic.ts: replaced assertCredits/CreditExhaustedError with checkWalletBalance/WalletExhaustedError - ai.ts: added requireCredits preHandler to all 11 AI routes (was 4) - billing.ts: /admin/allocate now calls creditWallet(); new GET /wallet route (balance + 20 last txns) - BillingPage: wallet card…0fd3320 -
feat(billing): enforce AI credit limits across all surfaces
CognxSafeTrack +1: Claude Sonnet 4.6- credit-guard.ts (worker): assertCredits() blocks AI when creditsUsed >= creditsLimit; Redis cache 60s prevents DB hit on every message - whatsapp-logic.ts: credit guard injected before handler chain; RECHARGE keyword bypasses the guard so users can always request a payment link - CommandHandler: new RECHARGE keyword sends payment URL or contact message - i18n: credit_exhausted + recharge_link + recharge_contact in 5 languages - requireCredits middleware (api): 402 on personalize-lesson, tts, generate-feedback, crm/generate-campaign - billing route: POST /v1/billing/admin/allocate (SUPER_ADMIN only) supports set/add actions + optional usage reset + Redis cache invalidation - ClientsManagementView: "Crédits IA" button (SUPER_ADMIN only) per org opens allocation modal with set/add/reset controls - BillingPage: red exhaustion banner when creditsUsed >= creditsLimit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6484585
-
feat(billing): complete billing system, push notifications, and tech debt fixes
CognxSafeTrack +1: Claude Sonnet 4.6- Usage tracking: instrument pedagogy, audio transcription, AI Agent mode (was silently untracked) - EmailHandler: implement templates 2 (password reset), 3 (quota alert), 4 (monthly report) - Quota alert: auto-queue email + push at 85% threshold via maybeQueueQuotaAlert - Push notifications: PushHandler + send-push BullMQ job; sendPushToUser scoped by organizationId to prevent cross-tenant reads - BillingPage: add credit recharge modal with 3 packs, null guard for missing orgId - i18n: add nav.billing key in fr/en/es/pt locales - Tests: billing.test.ts (10 tests), payments.test.ts (13 tests) - Tech debt: fix empty catch blocks in ai-pedagogy.ts, exclude usage tokens from CRM API response, pass organizationId to generateChat in AIAgentHandler - Docs: add backlog and business model to docs/business/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8280d7d
-
fix(api): secure storage of ai api keys by encrypting them
CognxSafeTrackcec25aa -
refactor(debt): resolve all 10 technical debt items from audit
CognxSafeTrack +1: Claude Sonnet 4.6- Redis: singleton pattern (lib/redis.ts) in api + worker, removes 16+ new Redis() calls - Meta token: systemUserTokenIssuedAt field + weekly cron monitor (warn @50d, critical @55d) - Rate limiting: @fastify/rate-limit global 200/min, login 10/min, forgot-password 3/min - Dead code: delete WhatsAppConnectButton.tsx, archive one-off scripts - Silent catches: logger.warn(err) on all backend catch blocks, console.warn on frontend - Meta API types: 5 typed interfaces replace 6 as any casts in organization.ts - Frontend logger: lib/logger.ts wraps Sentry (active when VITE_SENTRY_DSN is set) - Migration: add systemUserTokenIssuedAt + metaBusinessId to Organization schema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a966957
-
fix(prisma): add migration file for metaBusinessId + remove as-any casts
CognxSafeTrack +1: Claude Sonnet 4.6Creates the proper migration SQL for the metaBusinessId column that was applied directly via ALTER TABLE. Removes (org as any) casts now that the Prisma client is regenerated with the correct types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>53e72e6 -
feat(meta-status): sync daily limit + quality rating live from Meta phone number API
CognxSafeTrack +1: Claude Sonnet 4.6fetchMetaStatus now calls GET /{phoneNumberId}?fields=messaging_limit_tier,quality_rating to get the real tier from Meta (TIER_1K, TIER_10K, TIER_100K, UNLIMITED) instead of a hardcoded value inferred from WABA status. Frontend: TIER_LABELS map for human-readable labels, QualityDot badge (green/amber/red) displayed next to the limit. dailyLimitLabel now uses the real tier from the API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8aa43f4 -
feat(meta-status): store metaBusinessId for reliable business verification
CognxSafeTrack +1: Claude Sonnet 4.6Add optional `metaBusinessId` field to Organization (schema + shared-types). fetchMetaStatus now queries /{businessId}?fields=verification_status directly when metaBusinessId is stored — bypasses WABA edge permission restrictions that block test WABAs and certain system user token scopes. Direct setup form gains a "Business ID Meta" input that saves via PUT /organizations/:id. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>70a5a84 -
fix(meta-status): add on_behalf_of_business_info fallback for business verification
CognxSafeTrack +1: Claude Sonnet 4.6When the `business` edge on WABA returns nothing (system user token scope or test WABA restrictions), fall back to `on_behalf_of_business_info` + direct business node query for verification_status. Covers verified businesses whose WABA doesn't expose the business edge directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>bde43ec -
fix(meta-status): split combined Meta API call + fix business badge for test WABAs
CognxSafeTrack +1: Claude Sonnet 4.6Split `account_review_status,business{...}` into two independent calls so test WABAs (which forbid the `business` field) no longer poison WABA status. Business verification is now optional/non-fatal (wrapped in try/catch). BusinessBadge and BusinessVerificationCell now show "—" with neutral styling when `businessId` is undefined (field inaccessible) instead of misleading "Non vérifiée" amber warning + "Vérifier →" link. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>7e2166c
-
feat(meta): live Meta verification status + WhatsApp setup fixes
CognxSafeTrack +1: Claude Sonnet 4.6- GET /v1/organizations/:id/meta-status — fetches WABA account_review_status and business verification_status live from Meta Graph API, cached 1h in Redis - POST /v1/organizations/:id/meta-status/refresh — force-invalidates the cache - ClientsManagementView: replaced static metaVerificationStatus field with live per-org Meta status (WABA badge, Business badge, derived daily limit) - launchEmbeddedSignup: fixed race condition — now waits for both the OAuth code (FB.login) and WABA/phone IDs (WA_EMBEDDED_SIGNUP_EVENT) before resolving - /whatsapp-setup: exchanges OAuth code for long-lived Meta token (60 days) before storing; bypasses exchange if token already starts with EAA - OnboardingWizard: removed broken WhatsApp step (3 steps: welcome, legal, AI) - OrganizationSchema: removed non-existent DB fields (contractSigned, contractSignedAt, contractSignerName, metaVerificationStatus, dailyMessageLimit) that caused prisma.organization.create() to throw Unknown arg at runtime Re…bd9eb5b -
fix(seed): auto-register global WhatsApp phone number from env vars on startup
CognxSafeTrack +1: Claude Sonnet 4.6On API start, if WHATSAPP_PHONE_NUMBER_ID + WHATSAPP_ACCESS_TOKEN are set, finds the org whose systemUserToken (decrypted) matches the env token and upserts WhatsAppPhoneNumber so webhook routing works without manual setup. Fixes first/global organization breaking after multi-org migration. Idempotent — no-op if record already exists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>34691b4 -
feat: backlog P0→P3 — toast system, payments, tenant isolation, feedback handler, i18n parity
CognxSafeTrack +1: Claude Sonnet 4.6P0 — Critical fixes - Fix generate-feedback job silently dropped: register FeedbackHandler in worker index.ts - Wire FeedbackHandler: AI feedback, Day 11 TeamMember extraction, atomicity enforced P0 — Toast system (replace all alert()) - Add useToast hook + ToastContext + Toaster component - Replace every alert() across admin pages with toast.success/error/warning/info - Migrate all raw fetch() in pages to api.upload() / api.get() / api.post() P1 — Type safety - CrmAIAssistant: icon prop any → React.ElementType - useAudioRecorder: add onError callback instead of alert() - api.ts: add upload() method for multipart FormData P1 — Payment gateway - Add payment-gateway.ts: Orange Money + Wave initiation and webhook parsing - Rewrite payments.ts routes: initiate, status, webhook handlers with auto-enrollment on SUCCESS P1 — AI regression tests - Add apps/api/test/regression/ai-gold-standards.test.ts (12 tests, 5 suites) P2 — Visual recruitment (Day 11) - FeedbackHandler extracts TeamMem…6dd9bad
-
fix: resolve login 400 — auto-resolve organizationId from email when omitted
CognxSafeTrack +1: Claude Sonnet 4.6Backend now falls back to findUserByEmailOnly() when the frontend doesn't send organizationId (deployed admin still uses old build). Password verification still required — auth security unchanged. Also adds organizationId field to login form for first-time users, and removes last default-org-id references from tenant.tsx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4e2a593
-
feat: implement whatsapp templates management with security hardening, audit logs, and crm integration
CognxSafeTrack0f2f80a -
chore: stabilization, audit fixes and project synchronization
CognxSafeTrack7b0c22b
-
feat: implement bulk whatsapp campaigns with BullMQ and modular activation refactor (isCrmActive/isEdTechActive)
CognxSafeTracke286845 -
feat: centralize AI logic in @repo /ai-sdk, harmonize worker HTTP clients
CognxSafeTrack +1: Claude Sonnet 4.6Architecture refactoring: - Extract all AI providers (OpenAI, Gemini, ProviderRegistry, ffmpeg, search) from apps/api/src/services/ai/ into new packages/ai-sdk package - API's services/ai/index.ts is now a thin wrapper that injects prisma, redis and organization context into the shared AIService - Worker gains @repo/ai-sdk dependency; ai-pedagogy.ts delegates to aiService.generatePersonalizedLesson instead of duplicating logic - New apiClient (Axios, 30s timeout) and whatsappClient replace bare fetch calls in InboundHandler and MediaHandler for consistent error handling - Prisma seed fixed: Tracks now include organizationId for multi-tenant compliance Build: all 8 packages pass pnpm turbo run build from clean state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2849f33 -
feat(crm): optimize multimedia pipeline and harden contact schema
CognxSafeTrack- Refactored CrmConversationalDashboard into modular components and hooks (FileImporter, useAudioRecorder) - Centralized contact normalization and bulk import in ContactService - Implemented binary streaming for media proxy to reduce memory pressure - Hardened database schema with MediaType Enum for better data integrityc5fde49 -
feat: implement inbound audio/image transcription, secure media proxy, and client-side bulk contact import
CognxSafeTrack30d60ea
-
chore: CRM stabilization sprint completion
CognxSafeTrack- Refactor: Decomposed CrmConversationalDashboard into modular components (Inbox, AIAssistant, StatsHeader) - Fix: Centralized entity resolution with EntityResolver service (API & Worker) - Fix: Strict Prisma typing, removed 'as any' casts in organization and whatsapp routes - Fix: Implemented broadcast message persistence and accurate usage quota tracking - Schema: Added MessageStatus and status field to Message model4339e77 -
feat: implement WhatsApp broadcast system with BullMQ worker and rate limiting
CognxSafeTrackf000fc9 -
feat: implement AI campaign generation logic and frontend integration
CognxSafeTrack9f0410f -
chore: finalize Sprint P2 & P3 optimizations, baseline prisma migrations, and update technical audit docs
CognxSafeTrackcfbb685 -
feat: push notifications, CRM analytics, Fastify v5 upgrades, schema updates
CognxSafeTrack +1: Claude Sonnet 4.6- Add web push notification service and admin endpoints - Add CRM analytics component and conversational dashboard - Extend Contacts page with campaign management - Update AI and whatsapp routes (Fastify v5 compat, webhook fixes) - Extend Prisma schema with new models and indexes - Add CRM campaign prompt template Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b43e552
-
security: restrict CORS, enforce secrets validation, fix internal forwarding header
CognxSafeTrack89847a8 -
feat: complete final technical debt polishing (structured logs, stripe tests, and automated migrations)
CognxSafeTrack86731f4 -
fix: resolve test import errors and export AIService class
CognxSafeTrack459f379 -
fix: restore logger compatibility and fix major TS errors in services
CognxSafeTrack38df3bf -
fix: correct pino transport syntax and cleanup logging types
CognxSafeTrack0a6555d -
feat: implement Phase 4 (Quota management, centralized logging, and env sync)
CognxSafeTrackaec4d7f -
fix: resolve IDE errors and update Prisma schema for Phase 2
CognxSafeTrackf8274a4 -
feat: implement organization-specific API keys and tenant-aware webhooks (Phase 2)
CognxSafeTrackc37b68c -
audit: implement storage isolation and worker bridge hardening
CognxSafeTrack6019d2d
-
fix: resolve api build errors and multi-tenant unique key constraints
CognxSafeTracka37afe6
-
feat: migrate to multi-tenant SaaS architecture with JWT auth and BullMQ notifications
CognxSafeTrack2ab1980
-
feat: Premium Onboarding with Meta SDK and Technical Debt resolution
CognxSafeTrack6248bf4
-
feat: B2B SaaS Multi-tenant architecture & Tech Debt Resolution
CognxSafeTracke289c5c -
chore: stabilize whatsapp-worker with strict typing and multi-tenant hardening
CognxSafeTrack1a00f18
-
fix: stabilize API build by correcting Prisma naming and unused imports
CognxSafeTrack1a99598