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(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(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 -
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 -
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: 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(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(prompts): enforce language isolation in all AI prompts
CognxSafeTrack +1: Claude Sonnet 4.6Three issues caused language mixing for EN/ES/PT users: 1. feedback-base.md — language rule was at the bottom (line 31) after 30 lines of French context. Moved to line 1 with explicit per-language instructions. 2. action-feedback-standard.md — INVITATION DEEP-DIVE was hardcoded in prompt AND appended by buildFeedbackMessage → double deep-dive line, always in French. Prompt now tells AI NOT to include the invitation (handled externally). 3. personalized-lesson.md — language instruction buried in numbered list. Moved to line 1 with same explicit per-language format. WOLOF keeps the documented behavior: WOLOF standardisé with French translations for complex business terms in parentheses. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>1167236 -
fix(bot): eliminate double feedback, fix gemini model, fix multilingual prompts
CognxSafeTrack +1: Claude Sonnet 4.61. Double feedback (critical): Bridge was enqueuing both download-media AND handle-inbound for every audio/image message. Both paths independently transcribed the audio and called WhatsAppLogic.handleIncomingMessage → two generate-feedback jobs → user received two Coach responses. Fix: remove handle-inbound and send-message-direct from bridge for media. MediaHandler (download-media) already handles full pipeline. MediaHandler now sends a localized spinner after user lookup (sendSpinner flag). 2. gemini-1.5-pro → 404: gemini-1.5-pro is deprecated via Google API v1beta. Updated GeminiProvider to use gemini-2.0-flash for both flash and complex (pro) requests. 3. Multilingual deep-dive invitation (EN/ES/PT): action-feedback-standard.md only had FR and WO variants for the APPROFONDIR/SUITE invitation at end of feedback. Added EN, ES, PT — AI was mixing languages for English/Spanish/Portuguese users. 4. InboundHandler [object Object] bug: aiService.tran…5f0a436
-
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(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 -
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 -
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 -
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: deploy manual fixes for TS strict mode
CognxSafeTrack9b8717a -
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