Commit History
-
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
-
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 -
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 -
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(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 -
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): 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(build+worker): restore seedDatabase export + DISABLE_WORKER_CONSUMER guard
CognxSafeTrack +1: Claude Sonnet 4.61. packages/database/seed.ts: restore re-export of seedDatabase. moduleResolution:node ignores package.json exports field — TypeScript resolves @repo/database/seed to root seed.ts. Without the re-export, tsc sees no seedDatabase export → TS2339 build failure. Runtime still uses dist/src/seed.js (via exports field) which is compiled CJS with no TypeScript syntax. 2. apps/whatsapp-worker/src/index.ts: add DISABLE_WORKER_CONSUMER guard. Both Railway and HuggingFace workers share the same BullMQ/Redis queue. When HuggingFace wins a send-message job, it fails with SSL EPROTO (HF cannot reach graph.facebook.com). Setting DISABLE_WORKER_CONSUMER=true on HuggingFace keeps the bridge running (accepts forwarded webhooks) but stops it from consuming jobs — only Railway's worker sends to Meta. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>76f2a6a -
fix(database): point @repo /database/seed export to compiled dist
CognxSafeTrack +1: Claude Sonnet 4.6exports["./seed"] was pointing to src/seed.ts (raw TypeScript) which Node cannot parse in production → SyntaxError: Unexpected token ':'. Changed to dist/src/seed.js which is already compiled by tsc (src/**/* is in tsconfig include). Also reverted root seed.ts to pure script — the re-export we added was unused since exports now points to dist/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>3deace7 -
fix(bot): build error + multilingual sectors + UserProgress on enrollment
CognxSafeTrack +1: Claude Sonnet 4.6Build: - Revert CommandHandler to dynamic import for seedDatabase — TypeScript moduleResolution:node resolves @repo/database/seed to root seed.ts which had no exports. Added re-export of seedDatabase there as fix. OnboardingHandler: - LEGACY_SECTORS now has multilingual labels (FR/WOLOF/EN/ES/PT) - Sector list displayed in the user's chosen language at step 2 - Sector label saved to activity in user's language (not hardcoded FR) EnrollHandler: - Create UserProgress atomically alongside Enrollment on first enroll. Missing UserProgress caused NavigationHandler SUITE to throw "Record not found" on userProgress.update, blocking all navigation. - Welcome message now sent in user's language (was hardcoded FR) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b538b17 -
fix(audit): resolve technical debt + UX issues from 2026-05-11 audit
CognxSafeTrack +1: Claude Sonnet 4.6Backend: - Add missing DB indexes on Response.userId and Response.enrollmentId - Add 15s timeout with fallback on AI personalization in pedagogy.ts - Fix race condition in NavigationHandler with atomic $transaction - Fix OnboardingHandler hard reset: use FK cascades, 5-lang confirmation - Change seedDatabase to static import in CommandHandler - Add proper pagination to /enrollments admin endpoint UX/Admin: - Fix responsive grid on dashboard stats (1col mobile → 5col desktop) - Show filename + status label during file upload in ContactsPage - Add success/error toast with import summary on contact CSV/XLSX upload - Add inline form validation with field-level errors in ClientsManagementView - Wire up Copy button on AI-generated messages in ContactsPage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9fe2cc7 -
fix(content): correct wrong (FR) suffix in T2-ES, T3-PT, T5-PT titles
CognxSafeTrack +1: Claude Sonnet 4.6Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>1c3a0ab -
feat(db): add EN/ES/PT values to Language enum
CognxSafeTrack +1: Claude Sonnet 4.6Migration adds the three missing language values to the PostgreSQL Language enum. Required for the new translated tracks (T1-EN → T5-PT) to be stored and queried correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>442d9ba -
feat(content): add EN/ES/PT translations for all 5 training tracks
CognxSafeTrack +1: Claude Sonnet 4.615 new track files (T1→T5 × EN/ES/PT) generated by GPT-4o from the French source tracks. All text fields translated: title, lessonText, exercisePrompt, videoCaption, exerciseCriteria goal/hints, buttonsJson titles. IDs, URLs, weights and thresholds preserved exactly. audioUrl is null in all translated tracks — TTS auto-generates on first lesson delivery via the existing OpenAI tts-1 pipeline. OnboardingHandler now routes EN→T1-EN, ES→T1-ES, PT→T1-PT directly (no more FR fallback). The existing seed loader picks up all new files automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>018b222 -
feat(content): add AI track translation script for EN/ES/PT
CognxSafeTrack +1: Claude Sonnet 4.6Adds translate-tracks.ts — a GPT-4o script that reads all T*-FR.json track files and generates translated versions (T*-EN, T*-ES, T*-PT). Translates: title, lessonText, exercisePrompt, videoCaption, exerciseCriteria text fields, and buttonsJson titles. Preserves: IDs, URLs, dayNumbers, weights, thresholds, badges. Sets audioUrl=null so TTS auto-generates on first delivery. Commands: pnpm --filter @repo/database translate:tracks # run all pnpm --filter @repo/database translate:tracks:dry # preview only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>697c637 -
fix(bot): add missing Contact.language migration + fix onboarding blocking
CognxSafeTrack +1: Claude Sonnet 4.6- Migration: add Contact.language column (was in schema, never deployed — caused EntityResolver to crash on every message) - sendInteractiveButtonMessage: fallback to plain text when WhatsApp API rejects interactive (mirrors existing sendInteractiveListMessage behavior) - OnboardingHandler: guard mid-onboarding hard reset (no activity = resend language buttons instead of wiping data); correct welcome copy for new users Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>e6916d6 -
chore: cleanup old scripts and sync files
CognxSafeTrack4a46753 -
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): 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
-
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
-
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 -
fix: worker cleanup await, Stripe upsert completeness, redundant schema index
CognxSafeTrack +1: Claude Sonnet 4.6- cleanup.ts: cron callback now async/await so cleanWorkerTempFiles() errors are caught instead of silently dropped fire-and-forget - payments.ts: upsert update clause now includes status, amount, currency so a Stripe webhook replay fully idempotent instead of leaving stale fields - schema.prisma: remove redundant @@index([userId, trackId]) on UserProgress — the @@unique constraint already creates a B-tree index on those columns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>ec9b5da -
fix(db): add organizationId to seed script for multi-tenancy support
CognxSafeTrack2de03d4 -
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: execute Phase 1 of technical debt roadmap
CognxSafeTrack- Infra: Updated turbo.json to track Prisma schema changes - Infra: Automated 'prisma generate' in @repo/database build script - DB: Added strategic indexes on Message, Enrollment, and AnalyticsLog for performanceb168be2
-
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 inbound message tracking and CRM inbox view
CognxSafeTracke01e314 -
feat: implement BroadcastList model and 1-file-per-list import logic
CognxSafeTrack263f6e0 -
feat: add organization useCase selection to modal and backend
CognxSafeTrack55a1607 -
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
-
feat: implement PaaS modularity foundation (Contact model, modular OrganizationMode, and redesigned creation modal)
CognxSafeTrack3992613 -
feat: deploy user modifications to database and docker configuration
CognxSafeTrack192979e -
feat: complete final technical debt polishing (structured logs, stripe tests, and automated migrations)
CognxSafeTrack86731f4 -
fix: resolve IDE errors and update Prisma schema for Phase 2
CognxSafeTrackf8274a4
-
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
-
fix: use source-based imports (tsx) for both API and Worker to avoid dist resolution issues
CognxSafeTrack24e10ef -
deploy: finalize robust monorepo package configuration (v1.1.1)
CognxSafeTrack205ffab -
fix: use hybrid exports for database package to support both tsx and node runtimes
CognxSafeTrack53c6fed -
fix: point database package to dist for production runtime compatibility
CognxSafeTrack1141077 -
fix: add .js extensions to database exports for ESM production compatibility
CognxSafeTrack11f822c -
fix: exempt Organization model from tenant isolation
CognxSafeTrackffddff2