elevenlabs-webhook-nodejs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +27 -0
- package/.dockerignore +6 -0
- package/.env.example +43 -0
- package/BEFORE-PRODUCTION.md +32 -0
- package/Dockerfile +23 -0
- package/SYSTEM_OVERVIEW.md +942 -0
- package/SYSTEM_STATUS.md +332 -0
- package/backup_script/main.py +146 -0
- package/baileys/.dockerignore +7 -0
- package/baileys/Dockerfile +13 -0
- package/baileys/README.md +412 -0
- package/baileys/index.js +499 -0
- package/baileys/package-lock.json +2532 -0
- package/baileys/package.json +25 -0
- package/baileys/server.js +96 -0
- package/baileys/src/config.js +55 -0
- package/baileys/src/middleware/api-key.js +16 -0
- package/baileys/src/routes/accounts.js +51 -0
- package/baileys/src/routes/chats.js +103 -0
- package/baileys/src/routes/webhooks.js +34 -0
- package/baileys/src/services/account-store.js +259 -0
- package/baileys/src/services/webhook-dispatcher.js +161 -0
- package/baileys/src/services/worker-manager.js +597 -0
- package/baileys/src/utils/jid.js +79 -0
- package/baileys/src/utils/logger.js +16 -0
- package/baileys/src/utils/use-mongodb-auth-state.js +122 -0
- package/baileys/worker.js +721 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +84 -0
- package/dist/app.js.map +1 -0
- package/dist/config/agentPrompts.json +19 -0
- package/dist/config/database.d.ts +1 -0
- package/dist/config/database.js +26 -0
- package/dist/config/database.js.map +1 -0
- package/dist/config/env.d.ts +41 -0
- package/dist/config/env.js +81 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +73 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controllers/webhook.controller.d.ts +10 -0
- package/dist/controllers/webhook.controller.js +128 -0
- package/dist/controllers/webhook.controller.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/hooks/webhookValidator.d.ts +2 -0
- package/dist/hooks/webhookValidator.js +47 -0
- package/dist/hooks/webhookValidator.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/001_session-architecture.d.ts +3 -0
- package/dist/migrations/001_session-architecture.js +119 -0
- package/dist/migrations/001_session-architecture.js.map +1 -0
- package/dist/migrations/002_agent-ref.d.ts +3 -0
- package/dist/migrations/002_agent-ref.js +55 -0
- package/dist/migrations/002_agent-ref.js.map +1 -0
- package/dist/migrations/003_shift-schedule.d.ts +3 -0
- package/dist/migrations/003_shift-schedule.js +48 -0
- package/dist/migrations/003_shift-schedule.js.map +1 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.d.ts +3 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js +27 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js.map +1 -0
- package/dist/migrations/005_composite-baileys-chat-ids.d.ts +3 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js +47 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js.map +1 -0
- package/dist/migrations/migration.model.d.ts +18 -0
- package/dist/migrations/migration.model.js +45 -0
- package/dist/migrations/migration.model.js.map +1 -0
- package/dist/migrations/migration.routes.d.ts +2 -0
- package/dist/migrations/migration.routes.js +37 -0
- package/dist/migrations/migration.routes.js.map +1 -0
- package/dist/migrations/migration.runner.d.ts +19 -0
- package/dist/migrations/migration.runner.js +74 -0
- package/dist/migrations/migration.runner.js.map +1 -0
- package/dist/models/ConversationClaim.d.ts +13 -0
- package/dist/models/ConversationClaim.js +44 -0
- package/dist/models/ConversationClaim.js.map +1 -0
- package/dist/models/ConversationEvaluation.d.ts +23 -0
- package/dist/models/ConversationEvaluation.js +92 -0
- package/dist/models/ConversationEvaluation.js.map +1 -0
- package/dist/models/Summary.d.ts +37 -0
- package/dist/models/Summary.js +78 -0
- package/dist/models/Summary.js.map +1 -0
- package/dist/models/Transcription.d.ts +34 -0
- package/dist/models/Transcription.js +71 -0
- package/dist/models/Transcription.js.map +1 -0
- package/dist/models/WhatsAppRecipient.d.ts +23 -0
- package/dist/models/WhatsAppRecipient.js +53 -0
- package/dist/models/WhatsAppRecipient.js.map +1 -0
- package/dist/modules/admin/admin.routes.d.ts +2 -0
- package/dist/modules/admin/admin.routes.js +1966 -0
- package/dist/modules/admin/admin.routes.js.map +1 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js +158 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js.map +1 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js +204 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js.map +1 -0
- package/dist/modules/agents/agent.model.d.ts +38 -0
- package/dist/modules/agents/agent.model.js +92 -0
- package/dist/modules/agents/agent.model.js.map +1 -0
- package/dist/modules/agents/agent.routes.d.ts +2 -0
- package/dist/modules/agents/agent.routes.js +61 -0
- package/dist/modules/agents/agent.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.model.d.ts +76 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js +118 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.d.ts +2 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js +202 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.service.d.ts +53 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js +827 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js.map +1 -0
- package/dist/modules/auth/auth.model.d.ts +17 -0
- package/dist/modules/auth/auth.model.js +64 -0
- package/dist/modules/auth/auth.model.js.map +1 -0
- package/dist/modules/auth/auth.routes.d.ts +2 -0
- package/dist/modules/auth/auth.routes.js +202 -0
- package/dist/modules/auth/auth.routes.js.map +1 -0
- package/dist/modules/auth/auth.service.d.ts +28 -0
- package/dist/modules/auth/auth.service.js +183 -0
- package/dist/modules/auth/auth.service.js.map +1 -0
- package/dist/modules/auth/refresh-token.model.d.ts +13 -0
- package/dist/modules/auth/refresh-token.model.js +52 -0
- package/dist/modules/auth/refresh-token.model.js.map +1 -0
- package/dist/modules/billing/billing-alert.model.d.ts +16 -0
- package/dist/modules/billing/billing-alert.model.js +47 -0
- package/dist/modules/billing/billing-alert.model.js.map +1 -0
- package/dist/modules/billing/billing-period-snapshot.model.d.ts +35 -0
- package/dist/modules/billing/billing-period-snapshot.model.js +68 -0
- package/dist/modules/billing/billing-period-snapshot.model.js.map +1 -0
- package/dist/modules/billing/billing.model.d.ts +18 -0
- package/dist/modules/billing/billing.model.js +62 -0
- package/dist/modules/billing/billing.model.js.map +1 -0
- package/dist/modules/billing/billing.routes.d.ts +2 -0
- package/dist/modules/billing/billing.routes.js +63 -0
- package/dist/modules/billing/billing.routes.js.map +1 -0
- package/dist/modules/billing/billing.service.d.ts +69 -0
- package/dist/modules/billing/billing.service.js +498 -0
- package/dist/modules/billing/billing.service.js.map +1 -0
- package/dist/modules/billing/payment.model.d.ts +24 -0
- package/dist/modules/billing/payment.model.js +57 -0
- package/dist/modules/billing/payment.model.js.map +1 -0
- package/dist/modules/calls/call.model.d.ts +41 -0
- package/dist/modules/calls/call.model.js +97 -0
- package/dist/modules/calls/call.model.js.map +1 -0
- package/dist/modules/calls/call.routes.d.ts +2 -0
- package/dist/modules/calls/call.routes.js +103 -0
- package/dist/modules/calls/call.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.model.d.ts +45 -0
- package/dist/modules/campaigns/campaign.model.js +98 -0
- package/dist/modules/campaigns/campaign.model.js.map +1 -0
- package/dist/modules/campaigns/campaign.routes.d.ts +2 -0
- package/dist/modules/campaigns/campaign.routes.js +323 -0
- package/dist/modules/campaigns/campaign.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.service.d.ts +11 -0
- package/dist/modules/campaigns/campaign.service.js +86 -0
- package/dist/modules/campaigns/campaign.service.js.map +1 -0
- package/dist/modules/google-calendar/google-calendar.routes.d.ts +2 -0
- package/dist/modules/google-calendar/google-calendar.routes.js +32 -0
- package/dist/modules/google-calendar/google-calendar.routes.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call-config.model.d.ts +20 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js +68 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call.routes.d.ts +2 -0
- package/dist/modules/inbound-call/inbound-call.routes.js +243 -0
- package/dist/modules/inbound-call/inbound-call.routes.js.map +1 -0
- package/dist/modules/leads/lead.model.d.ts +24 -0
- package/dist/modules/leads/lead.model.js +54 -0
- package/dist/modules/leads/lead.model.js.map +1 -0
- package/dist/modules/leads/lead.routes.d.ts +2 -0
- package/dist/modules/leads/lead.routes.js +201 -0
- package/dist/modules/leads/lead.routes.js.map +1 -0
- package/dist/modules/plans/plan.model.d.ts +25 -0
- package/dist/modules/plans/plan.model.js +59 -0
- package/dist/modules/plans/plan.model.js.map +1 -0
- package/dist/modules/surveys/survey.model.d.ts +30 -0
- package/dist/modules/surveys/survey.model.js +75 -0
- package/dist/modules/surveys/survey.model.js.map +1 -0
- package/dist/modules/surveys/survey.routes.d.ts +2 -0
- package/dist/modules/surveys/survey.routes.js +153 -0
- package/dist/modules/surveys/survey.routes.js.map +1 -0
- package/dist/modules/tenants/tenant.model.d.ts +86 -0
- package/dist/modules/tenants/tenant.model.js +127 -0
- package/dist/modules/tenants/tenant.model.js.map +1 -0
- package/dist/modules/tenants/tenant.routes.d.ts +2 -0
- package/dist/modules/tenants/tenant.routes.js +65 -0
- package/dist/modules/tenants/tenant.routes.js.map +1 -0
- package/dist/modules/users/user.routes.d.ts +2 -0
- package/dist/modules/users/user.routes.js +106 -0
- package/dist/modules/users/user.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.d.ts +20 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js +85 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.d.ts +11 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js +360 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.routes.d.ts +2 -0
- package/dist/modules/webhooks/elevenlabs.routes.js +34 -0
- package/dist/modules/webhooks/elevenlabs.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.service.d.ts +6 -0
- package/dist/modules/webhooks/elevenlabs.service.js +512 -0
- package/dist/modules/webhooks/elevenlabs.service.js.map +1 -0
- package/dist/modules/webhooks/unipile.routes.d.ts +2 -0
- package/dist/modules/webhooks/unipile.routes.js +780 -0
- package/dist/modules/webhooks/unipile.routes.js.map +1 -0
- package/dist/modules/whatsapp/appointment.model.d.ts +27 -0
- package/dist/modules/whatsapp/appointment.model.js +58 -0
- package/dist/modules/whatsapp/appointment.model.js.map +1 -0
- package/dist/modules/whatsapp/operator-request.model.d.ts +29 -0
- package/dist/modules/whatsapp/operator-request.model.js +65 -0
- package/dist/modules/whatsapp/operator-request.model.js.map +1 -0
- package/dist/modules/whatsapp/stt-usage.model.d.ts +16 -0
- package/dist/modules/whatsapp/stt-usage.model.js +53 -0
- package/dist/modules/whatsapp/stt-usage.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js +55 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js +54 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-message.model.d.ts +30 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js +52 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-session.model.d.ts +33 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js +65 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp.routes.d.ts +2 -0
- package/dist/modules/whatsapp/whatsapp.routes.js +1237 -0
- package/dist/modules/whatsapp/whatsapp.routes.js.map +1 -0
- package/dist/plugins/cors.d.ts +3 -0
- package/dist/plugins/cors.js +11 -0
- package/dist/plugins/cors.js.map +1 -0
- package/dist/plugins/jwt.d.ts +3 -0
- package/dist/plugins/jwt.js +22 -0
- package/dist/plugins/jwt.js.map +1 -0
- package/dist/plugins/rawBody.d.ts +3 -0
- package/dist/plugins/rawBody.js +16 -0
- package/dist/plugins/rawBody.js.map +1 -0
- package/dist/plugins/rbac.d.ts +5 -0
- package/dist/plugins/rbac.js +29 -0
- package/dist/plugins/rbac.js.map +1 -0
- package/dist/routes/admin.routes.d.ts +2 -0
- package/dist/routes/admin.routes.js +169 -0
- package/dist/routes/admin.routes.js.map +1 -0
- package/dist/routes/health.routes.d.ts +2 -0
- package/dist/routes/health.routes.js +16 -0
- package/dist/routes/health.routes.js.map +1 -0
- package/dist/routes/webhook.routes.d.ts +2 -0
- package/dist/routes/webhook.routes.js +17 -0
- package/dist/routes/webhook.routes.js.map +1 -0
- package/dist/services/ai/base.ai.d.ts +19 -0
- package/dist/services/ai/base.ai.js +120 -0
- package/dist/services/ai/base.ai.js.map +1 -0
- package/dist/services/ai/gemini.service.d.ts +11 -0
- package/dist/services/ai/gemini.service.js +43 -0
- package/dist/services/ai/gemini.service.js.map +1 -0
- package/dist/services/ai/index.d.ts +2 -0
- package/dist/services/ai/index.js +26 -0
- package/dist/services/ai/index.js.map +1 -0
- package/dist/services/ai/openai.service.d.ts +11 -0
- package/dist/services/ai/openai.service.js +50 -0
- package/dist/services/ai/openai.service.js.map +1 -0
- package/dist/services/elevenlabs.service.d.ts +52 -0
- package/dist/services/elevenlabs.service.js +447 -0
- package/dist/services/elevenlabs.service.js.map +1 -0
- package/dist/services/google-calendar.service.d.ts +60 -0
- package/dist/services/google-calendar.service.js +494 -0
- package/dist/services/google-calendar.service.js.map +1 -0
- package/dist/services/inbound-call-schedule.service.d.ts +11 -0
- package/dist/services/inbound-call-schedule.service.js +162 -0
- package/dist/services/inbound-call-schedule.service.js.map +1 -0
- package/dist/services/netgsm.service.d.ts +41 -0
- package/dist/services/netgsm.service.js +89 -0
- package/dist/services/netgsm.service.js.map +1 -0
- package/dist/services/unipile.service.d.ts +41 -0
- package/dist/services/unipile.service.js +149 -0
- package/dist/services/unipile.service.js.map +1 -0
- package/dist/services/whatsapp-agent.service.d.ts +139 -0
- package/dist/services/whatsapp-agent.service.js +2055 -0
- package/dist/services/whatsapp-agent.service.js.map +1 -0
- package/dist/services/whatsapp.service.d.ts +26 -0
- package/dist/services/whatsapp.service.js +206 -0
- package/dist/services/whatsapp.service.js.map +1 -0
- package/dist/templates/index.d.ts +39 -0
- package/dist/templates/index.js +35 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/receptionist.d.ts +2 -0
- package/dist/templates/receptionist.js +39 -0
- package/dist/templates/receptionist.js.map +1 -0
- package/dist/templates/survey.d.ts +2 -0
- package/dist/templates/survey.js +41 -0
- package/dist/templates/survey.js.map +1 -0
- package/dist/types/index.d.ts +173 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker-compose.nestjs.yml +89 -0
- package/docker-compose.yml +78 -0
- package/docs/AI_AGENT_ENHANCEMENT_PLAN.md +164 -0
- package/docs/API.md +1193 -0
- package/docs/API_ENDPOINTS.md +344 -0
- package/docs/ARCHITECTURE.md +305 -0
- package/docs/AUTH_API.md +252 -0
- package/docs/BILLING_SMS_ALERTS.md +94 -0
- package/docs/CHAT_ASSIGNMENT_SYSTEM.md +118 -0
- package/docs/CLIENT_TOOLS_AND_FEATURES.md +337 -0
- package/docs/ELEVENLABS_WEBHOOK_TOOLS.md +644 -0
- package/docs/FRONTEND_CHECKLIST.md +227 -0
- package/docs/IMPLEMENTATION_STATUS.md +470 -0
- package/docs/MIGRATION_GUIDE.md +96 -0
- package/docs/MISSINGS_REPORT.md +507 -0
- package/docs/NESTJS_MIGRATION_REFERENCE.md +5136 -0
- package/docs/PROJECT_DESCRIPTION.md +1038 -0
- package/docs/SCALING.md +148 -0
- package/docs/SESSION_SUMMARY_2026_03_17.md +135 -0
- package/docs/WHATSAPP_AGENT.md +1086 -0
- package/docs/architecture/00-SYSTEM-OVERVIEW.md +318 -0
- package/docs/architecture/01-DATABASE-SCHEMA.md +2564 -0
- package/docs/architecture/02-AUTHENTICATION.md +1040 -0
- package/docs/architecture/03-MULTI-CLINIC.md +742 -0
- package/docs/architecture/04-WHATSAPP-AGENT.md +608 -0
- package/docs/architecture/05-OPERATOR-WORKFLOW.md +444 -0
- package/docs/architecture/06-BAILEYS-MICROSERVICE.md +616 -0
- package/docs/architecture/07-APPOINTMENTS.md +849 -0
- package/docs/architecture/08-VOICE-CALLS.md +470 -0
- package/docs/architecture/09-OUTBOUND-CAMPAIGNS.md +542 -0
- package/docs/architecture/10-CLIENT-TOOLS.md +665 -0
- package/docs/architecture/11-BILLING.md +458 -0
- package/docs/architecture/12-SECURITY.md +216 -0
- package/docs/architecture/13-LOGGING-AUDIT.md +549 -0
- package/docs/architecture/14-META-BUSINESS-API.md +454 -0
- package/docs/architecture/15-AI-MODULE.md +479 -0
- package/docs/architecture/16-BACKGROUND-JOBS.md +469 -0
- package/docs/architecture/17-REALTIME-LIVECHAT.md +447 -0
- package/docs/architecture/18-FILE-STORAGE.md +410 -0
- package/docs/architecture/19-PATIENTS.md +1034 -0
- package/docs/architecture/20-TREATMENTS-AND-PLANS.md +774 -0
- package/docs/architecture/21-BEFORE-AFTER-PHOTOS.md +519 -0
- package/docs/database.md +456 -0
- package/docs/ornek-randevu-onay.csv +3 -0
- package/ecosystem.config.js +16 -0
- package/elevenlabs-convai-api-reference.md +1171 -0
- package/frontend/.dockerignore +2 -0
- package/frontend/Dockerfile +24 -0
- package/frontend/README.md +75 -0
- package/frontend/components.json +25 -0
- package/frontend/eslint.config.js +23 -0
- package/frontend/index.html +13 -0
- package/frontend/nginx.conf +37 -0
- package/frontend/package-lock.json +8709 -0
- package/frontend/package.json +71 -0
- package/frontend/public/favicon.svg +1 -0
- package/frontend/public/icons.svg +24 -0
- package/frontend/src/App.tsx +125 -0
- package/frontend/src/components/error-boundary.tsx +50 -0
- package/frontend/src/components/shared/activity-timeline.tsx +66 -0
- package/frontend/src/components/shared/appointment-calendar.css +80 -0
- package/frontend/src/components/shared/appointment-calendar.tsx +245 -0
- package/frontend/src/components/shared/chat-bubble.tsx +72 -0
- package/frontend/src/components/shared/combobox.tsx +119 -0
- package/frontend/src/components/shared/confirm-dialog.tsx +57 -0
- package/frontend/src/components/shared/data-table-pagination.tsx +97 -0
- package/frontend/src/components/shared/data-table-toolbar.tsx +39 -0
- package/frontend/src/components/shared/empty-state.tsx +27 -0
- package/frontend/src/components/shared/file-upload.tsx +140 -0
- package/frontend/src/components/shared/index.ts +20 -0
- package/frontend/src/components/shared/language-switcher.tsx +29 -0
- package/frontend/src/components/shared/notification-dropdown.tsx +115 -0
- package/frontend/src/components/shared/page-header.tsx +23 -0
- package/frontend/src/components/shared/stat-card.tsx +37 -0
- package/frontend/src/components/shared/status-badge.tsx +70 -0
- package/frontend/src/components/shared/status-dot.tsx +43 -0
- package/frontend/src/components/ui/alert-dialog.tsx +187 -0
- package/frontend/src/components/ui/alert.tsx +76 -0
- package/frontend/src/components/ui/avatar.tsx +109 -0
- package/frontend/src/components/ui/badge.tsx +52 -0
- package/frontend/src/components/ui/breadcrumb.tsx +125 -0
- package/frontend/src/components/ui/button.tsx +60 -0
- package/frontend/src/components/ui/calendar.tsx +219 -0
- package/frontend/src/components/ui/card.tsx +103 -0
- package/frontend/src/components/ui/chart.tsx +371 -0
- package/frontend/src/components/ui/checkbox.tsx +29 -0
- package/frontend/src/components/ui/collapsible.tsx +19 -0
- package/frontend/src/components/ui/command.tsx +194 -0
- package/frontend/src/components/ui/dialog.tsx +158 -0
- package/frontend/src/components/ui/dropdown-menu.tsx +268 -0
- package/frontend/src/components/ui/input-group.tsx +156 -0
- package/frontend/src/components/ui/input.tsx +20 -0
- package/frontend/src/components/ui/label.tsx +20 -0
- package/frontend/src/components/ui/pagination.tsx +130 -0
- package/frontend/src/components/ui/popover.tsx +90 -0
- package/frontend/src/components/ui/progress.tsx +83 -0
- package/frontend/src/components/ui/radio-group.tsx +36 -0
- package/frontend/src/components/ui/scroll-area.tsx +54 -0
- package/frontend/src/components/ui/select.tsx +199 -0
- package/frontend/src/components/ui/separator.tsx +23 -0
- package/frontend/src/components/ui/sheet.tsx +138 -0
- package/frontend/src/components/ui/sidebar.tsx +723 -0
- package/frontend/src/components/ui/skeleton.tsx +13 -0
- package/frontend/src/components/ui/sonner.tsx +47 -0
- package/frontend/src/components/ui/switch.tsx +30 -0
- package/frontend/src/components/ui/table.tsx +114 -0
- package/frontend/src/components/ui/tabs.tsx +82 -0
- package/frontend/src/components/ui/textarea.tsx +18 -0
- package/frontend/src/components/ui/toggle-group.tsx +89 -0
- package/frontend/src/components/ui/toggle.tsx +43 -0
- package/frontend/src/components/ui/tooltip.tsx +64 -0
- package/frontend/src/hooks/use-mobile.ts +19 -0
- package/frontend/src/i18n/index.ts +20 -0
- package/frontend/src/i18n/locales/en.json +786 -0
- package/frontend/src/i18n/locales/tr.json +786 -0
- package/frontend/src/index.css +134 -0
- package/frontend/src/layouts/admin-layout.tsx +111 -0
- package/frontend/src/layouts/app-header.tsx +130 -0
- package/frontend/src/layouts/app-layout.tsx +19 -0
- package/frontend/src/layouts/app-sidebar.tsx +212 -0
- package/frontend/src/layouts/auth-guard.tsx +31 -0
- package/frontend/src/layouts/mobile-sidebar.tsx +120 -0
- package/frontend/src/lib/api.ts +68 -0
- package/frontend/src/lib/hooks/use-appointments.ts +224 -0
- package/frontend/src/lib/hooks/use-availability-blocks.ts +83 -0
- package/frontend/src/lib/hooks/use-chats.ts +236 -0
- package/frontend/src/lib/hooks/use-clinic-detail.ts +171 -0
- package/frontend/src/lib/hooks/use-clinics.ts +37 -0
- package/frontend/src/lib/hooks/use-doctor-calendar.ts +80 -0
- package/frontend/src/lib/hooks/use-examinations.ts +89 -0
- package/frontend/src/lib/hooks/use-medical-history.ts +52 -0
- package/frontend/src/lib/hooks/use-patients.ts +296 -0
- package/frontend/src/lib/hooks/use-photo-sets.ts +118 -0
- package/frontend/src/lib/hooks/use-treatment-plans.ts +152 -0
- package/frontend/src/lib/hooks/use-treatments.ts +125 -0
- package/frontend/src/lib/hooks/use-users.ts +172 -0
- package/frontend/src/lib/utils.ts +6 -0
- package/frontend/src/main.tsx +17 -0
- package/frontend/src/pages/admin/agents/detail.tsx +774 -0
- package/frontend/src/pages/admin/agents/import.tsx +280 -0
- package/frontend/src/pages/admin/agents/index.tsx +245 -0
- package/frontend/src/pages/admin/ai-playground.tsx +543 -0
- package/frontend/src/pages/appointments/create-appointment-dialog.tsx +390 -0
- package/frontend/src/pages/appointments/index.tsx +860 -0
- package/frontend/src/pages/audit/index.tsx +24 -0
- package/frontend/src/pages/auth/login.tsx +194 -0
- package/frontend/src/pages/billing/index.tsx +24 -0
- package/frontend/src/pages/calendar/index.tsx +704 -0
- package/frontend/src/pages/calendar-connections/index.tsx +295 -0
- package/frontend/src/pages/calls/index.tsx +24 -0
- package/frontend/src/pages/campaigns/index.tsx +24 -0
- package/frontend/src/pages/chats/index.tsx +981 -0
- package/frontend/src/pages/clinics/index.tsx +224 -0
- package/frontend/src/pages/clinics/settings.tsx +412 -0
- package/frontend/src/pages/components-showcase.tsx +773 -0
- package/frontend/src/pages/connections/index.tsx +328 -0
- package/frontend/src/pages/dashboard.tsx +50 -0
- package/frontend/src/pages/leads/index.tsx +24 -0
- package/frontend/src/pages/my-schedule/index.tsx +496 -0
- package/frontend/src/pages/patients/create-patient-dialog.tsx +358 -0
- package/frontend/src/pages/patients/detail.tsx +1195 -0
- package/frontend/src/pages/patients/edit-patient-dialog.tsx +387 -0
- package/frontend/src/pages/patients/examinations-tab.tsx +460 -0
- package/frontend/src/pages/patients/index.tsx +381 -0
- package/frontend/src/pages/patients/medical-history-dialog.tsx +207 -0
- package/frontend/src/pages/patients/photo-sets-tab.tsx +616 -0
- package/frontend/src/pages/patients/timeline-tab.tsx +164 -0
- package/frontend/src/pages/patients/treatment-plans-tab.tsx +598 -0
- package/frontend/src/pages/phone-numbers/detail.tsx +427 -0
- package/frontend/src/pages/phone-numbers/index.tsx +455 -0
- package/frontend/src/pages/platform/index.tsx +454 -0
- package/frontend/src/pages/settings/index.tsx +126 -0
- package/frontend/src/pages/treatments/index.tsx +487 -0
- package/frontend/src/pages/users/doctor-profile.tsx +672 -0
- package/frontend/src/pages/users/edit.tsx +329 -0
- package/frontend/src/pages/users/index.tsx +407 -0
- package/frontend/src/pages/validation/index.tsx +24 -0
- package/frontend/src/stores/auth.store.ts +108 -0
- package/frontend/src/stores/ui.store.ts +41 -0
- package/frontend/tsconfig.app.json +32 -0
- package/frontend/tsconfig.json +13 -0
- package/frontend/tsconfig.node.json +26 -0
- package/frontend/vite.config.ts +29 -0
- package/nestjs/.dockerignore +5 -0
- package/nestjs/.env.docker +64 -0
- package/nestjs/.prettierrc +4 -0
- package/nestjs/Dockerfile +36 -0
- package/nestjs/README.md +98 -0
- package/nestjs/eslint.config.mjs +35 -0
- package/nestjs/nest-cli.json +8 -0
- package/nestjs/package-lock.json +13390 -0
- package/nestjs/package.json +114 -0
- package/nestjs/prisma/migrations/20260409161536_add_message_metadata_fields/migration.sql +1746 -0
- package/nestjs/prisma/migrations/20260410140436_add_agent_ai_fields/migration.sql +36 -0
- package/nestjs/prisma/migrations/20260410175519_add_agent_clinic_assignments/migration.sql +21 -0
- package/nestjs/prisma/migrations/20260412094344_make_agent_tenant_optional/migration.sql +2 -0
- package/nestjs/prisma/migrations/20260412110008_add_admin_chat_sessions/migration.sql +47 -0
- package/nestjs/prisma/migrations/migration_lock.toml +3 -0
- package/nestjs/prisma/schema.prisma +1843 -0
- package/nestjs/prisma/seed.ts +375 -0
- package/nestjs/prisma.config.ts +14 -0
- package/nestjs/src/admin/admin.controller.ts +27 -0
- package/nestjs/src/admin/admin.module.ts +16 -0
- package/nestjs/src/admin/admin.service.ts +91 -0
- package/nestjs/src/admin/ai-chat-session.service.ts +454 -0
- package/nestjs/src/admin/ai-test.controller.ts +191 -0
- package/nestjs/src/admin/superadmin.controller.ts +106 -0
- package/nestjs/src/agents/agents.controller.ts +262 -0
- package/nestjs/src/agents/agents.module.ts +13 -0
- package/nestjs/src/agents/agents.service.ts +733 -0
- package/nestjs/src/agents/dto/create-agent.dto.ts +99 -0
- package/nestjs/src/agents/dto/index.ts +2 -0
- package/nestjs/src/agents/dto/update-agent.dto.ts +148 -0
- package/nestjs/src/app.module.ts +115 -0
- package/nestjs/src/appointment-validation/appointment-validation.controller.ts +194 -0
- package/nestjs/src/appointment-validation/appointment-validation.module.ts +16 -0
- package/nestjs/src/appointment-validation/appointment-validation.service.ts +450 -0
- package/nestjs/src/appointment-validation/dto/create-batch.dto.ts +105 -0
- package/nestjs/src/appointment-validation/dto/index.ts +1 -0
- package/nestjs/src/appointment-validation/processors/validation-dispatch.processor.ts +26 -0
- package/nestjs/src/appointment-validation/processors/validation-sync.processor.ts +23 -0
- package/nestjs/src/appointments/appointments.controller.ts +268 -0
- package/nestjs/src/appointments/appointments.module.ts +13 -0
- package/nestjs/src/appointments/appointments.service.ts +773 -0
- package/nestjs/src/appointments/dto/create-appointment.dto.ts +72 -0
- package/nestjs/src/appointments/dto/index.ts +4 -0
- package/nestjs/src/appointments/dto/query-appointments.dto.ts +60 -0
- package/nestjs/src/appointments/dto/update-appointment.dto.ts +43 -0
- package/nestjs/src/appointments/dto/update-status.dto.ts +18 -0
- package/nestjs/src/appointments/processors/reminder.processor.ts +243 -0
- package/nestjs/src/audit/audit.controller.ts +84 -0
- package/nestjs/src/audit/audit.decorator.ts +20 -0
- package/nestjs/src/audit/audit.interceptor.ts +67 -0
- package/nestjs/src/audit/audit.interfaces.ts +15 -0
- package/nestjs/src/audit/audit.module.ts +12 -0
- package/nestjs/src/audit/audit.service.ts +177 -0
- package/nestjs/src/auth/auth.controller.ts +116 -0
- package/nestjs/src/auth/auth.module.ts +25 -0
- package/nestjs/src/auth/auth.service.ts +612 -0
- package/nestjs/src/auth/dto/change-password.dto.ts +13 -0
- package/nestjs/src/auth/dto/forgot-password.dto.ts +8 -0
- package/nestjs/src/auth/dto/index.ts +6 -0
- package/nestjs/src/auth/dto/login.dto.ts +13 -0
- package/nestjs/src/auth/dto/refresh.dto.ts +8 -0
- package/nestjs/src/auth/dto/register.dto.ts +28 -0
- package/nestjs/src/auth/dto/reset-password.dto.ts +13 -0
- package/nestjs/src/auth/permissions.config.ts +85 -0
- package/nestjs/src/availability-blocks/availability-blocks.controller.ts +83 -0
- package/nestjs/src/availability-blocks/availability-blocks.module.ts +10 -0
- package/nestjs/src/availability-blocks/availability-blocks.service.ts +202 -0
- package/nestjs/src/billing/billing.controller.ts +104 -0
- package/nestjs/src/billing/billing.module.ts +12 -0
- package/nestjs/src/billing/billing.service.ts +398 -0
- package/nestjs/src/billing/dto/index.ts +2 -0
- package/nestjs/src/billing/dto/query-billing.dto.ts +29 -0
- package/nestjs/src/billing/dto/record-payment.dto.ts +60 -0
- package/nestjs/src/billing/processors/alerts.processor.ts +216 -0
- package/nestjs/src/billing/processors/snapshot.processor.ts +181 -0
- package/nestjs/src/calls/calls.controller.ts +92 -0
- package/nestjs/src/calls/calls.module.ts +10 -0
- package/nestjs/src/calls/calls.service.ts +359 -0
- package/nestjs/src/calls/dto/index.ts +1 -0
- package/nestjs/src/calls/dto/query-calls.dto.ts +46 -0
- package/nestjs/src/clinics/clinics.controller.ts +226 -0
- package/nestjs/src/clinics/clinics.module.ts +11 -0
- package/nestjs/src/clinics/clinics.service.ts +203 -0
- package/nestjs/src/clinics/dto/create-clinic.dto.ts +40 -0
- package/nestjs/src/clinics/dto/create-meta-connection.dto.ts +44 -0
- package/nestjs/src/clinics/dto/index.ts +4 -0
- package/nestjs/src/clinics/dto/update-clinic.dto.ts +133 -0
- package/nestjs/src/clinics/dto/update-meta-connection.dto.ts +22 -0
- package/nestjs/src/clinics/meta-connections.service.ts +210 -0
- package/nestjs/src/common/decorators/accessible-clinics.decorator.ts +9 -0
- package/nestjs/src/common/decorators/current-user.decorator.ts +10 -0
- package/nestjs/src/common/decorators/features.decorator.ts +7 -0
- package/nestjs/src/common/decorators/index.ts +4 -0
- package/nestjs/src/common/decorators/permissions.decorator.ts +13 -0
- package/nestjs/src/common/gateways/events.gateway.ts +157 -0
- package/nestjs/src/common/guards/clinic-access.guard.ts +40 -0
- package/nestjs/src/common/guards/features.guard.ts +38 -0
- package/nestjs/src/common/guards/index.ts +5 -0
- package/nestjs/src/common/guards/jwt-auth.guard.ts +54 -0
- package/nestjs/src/common/guards/permissions.guard.ts +50 -0
- package/nestjs/src/common/guards/superadmin.guard.ts +35 -0
- package/nestjs/src/common/interceptors/request-context.interceptor.ts +32 -0
- package/nestjs/src/common/interceptors/superadmin-tenant.interceptor.ts +42 -0
- package/nestjs/src/common/interfaces/jwt-payload.interface.ts +11 -0
- package/nestjs/src/config/config.module.ts +13 -0
- package/nestjs/src/database/database.module.ts +9 -0
- package/nestjs/src/database/prisma.service.ts +20 -0
- package/nestjs/src/database/tenant-context.ts +27 -0
- package/nestjs/src/google-calendar/google-calendar.controller.ts +259 -0
- package/nestjs/src/google-calendar/google-calendar.module.ts +10 -0
- package/nestjs/src/google-calendar/google-calendar.service.ts +811 -0
- package/nestjs/src/integrations/ai/ai.interface.ts +74 -0
- package/nestjs/src/integrations/ai/ai.module.ts +11 -0
- package/nestjs/src/integrations/ai/ai.service.ts +148 -0
- package/nestjs/src/integrations/ai/providers/anthropic.provider.ts +146 -0
- package/nestjs/src/integrations/ai/providers/openai.provider.ts +158 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.module.ts +8 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.service.ts +226 -0
- package/nestjs/src/integrations/encryption/encryption.module.ts +9 -0
- package/nestjs/src/integrations/encryption/encryption.service.ts +31 -0
- package/nestjs/src/integrations/image/image.module.ts +9 -0
- package/nestjs/src/integrations/image/image.service.ts +61 -0
- package/nestjs/src/integrations/meta-business/meta-business.module.ts +10 -0
- package/nestjs/src/integrations/meta-business/meta-instagram.service.ts +94 -0
- package/nestjs/src/integrations/meta-business/meta-webhook.service.ts +52 -0
- package/nestjs/src/integrations/meta-business/meta-whatsapp.service.ts +254 -0
- package/nestjs/src/integrations/minio/minio.module.ts +9 -0
- package/nestjs/src/integrations/minio/minio.service.ts +88 -0
- package/nestjs/src/integrations/netgsm/netgsm.module.ts +8 -0
- package/nestjs/src/integrations/netgsm/netgsm.service.ts +17 -0
- package/nestjs/src/integrations/tektippay/tektippay.module.ts +8 -0
- package/nestjs/src/integrations/tektippay/tektippay.service.ts +23 -0
- package/nestjs/src/leads/dto/index.ts +2 -0
- package/nestjs/src/leads/dto/query-leads.dto.ts +50 -0
- package/nestjs/src/leads/dto/update-lead.dto.ts +26 -0
- package/nestjs/src/leads/leads.controller.ts +184 -0
- package/nestjs/src/leads/leads.module.ts +10 -0
- package/nestjs/src/leads/leads.service.ts +375 -0
- package/nestjs/src/logging/logging.controller.ts +82 -0
- package/nestjs/src/logging/logging.module.ts +11 -0
- package/nestjs/src/logging/logging.service.ts +180 -0
- package/nestjs/src/main.ts +86 -0
- package/nestjs/src/outbound-campaigns/dto/create-campaign.dto.ts +47 -0
- package/nestjs/src/outbound-campaigns/dto/index.ts +3 -0
- package/nestjs/src/outbound-campaigns/dto/update-campaign.dto.ts +31 -0
- package/nestjs/src/outbound-campaigns/dto/upload-entries.dto.ts +35 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.controller.ts +307 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.module.ts +13 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.service.ts +471 -0
- package/nestjs/src/outbound-campaigns/processors/campaign-dispatch.processor.ts +366 -0
- package/nestjs/src/patients/documents.service.ts +231 -0
- package/nestjs/src/patients/dto/create-examination.dto.ts +34 -0
- package/nestjs/src/patients/dto/create-note.dto.ts +14 -0
- package/nestjs/src/patients/dto/create-patient.dto.ts +86 -0
- package/nestjs/src/patients/dto/create-photo-set.dto.ts +32 -0
- package/nestjs/src/patients/dto/index.ts +10 -0
- package/nestjs/src/patients/dto/update-examination.dto.ts +29 -0
- package/nestjs/src/patients/dto/update-medical-history.dto.ts +47 -0
- package/nestjs/src/patients/dto/update-patient.dto.ts +87 -0
- package/nestjs/src/patients/dto/update-photo-set.dto.ts +28 -0
- package/nestjs/src/patients/dto/upload-document.dto.ts +31 -0
- package/nestjs/src/patients/dto/upload-photo.dto.ts +27 -0
- package/nestjs/src/patients/examinations.service.ts +271 -0
- package/nestjs/src/patients/medical-history.service.ts +149 -0
- package/nestjs/src/patients/notes.service.ts +172 -0
- package/nestjs/src/patients/patients.controller.ts +485 -0
- package/nestjs/src/patients/patients.module.ts +22 -0
- package/nestjs/src/patients/patients.service.ts +412 -0
- package/nestjs/src/patients/photo-sets.service.ts +389 -0
- package/nestjs/src/phone-numbers/dto/create-phone-number.dto.ts +57 -0
- package/nestjs/src/phone-numbers/dto/index.ts +3 -0
- package/nestjs/src/phone-numbers/dto/update-phone-number.dto.ts +38 -0
- package/nestjs/src/phone-numbers/dto/update-schedule.dto.ts +39 -0
- package/nestjs/src/phone-numbers/phone-numbers.controller.ts +125 -0
- package/nestjs/src/phone-numbers/phone-numbers.module.ts +10 -0
- package/nestjs/src/phone-numbers/phone-numbers.service.ts +209 -0
- package/nestjs/src/plans/dto/create-plan.dto.ts +70 -0
- package/nestjs/src/plans/dto/index.ts +2 -0
- package/nestjs/src/plans/dto/update-plan.dto.ts +80 -0
- package/nestjs/src/plans/plans.controller.ts +50 -0
- package/nestjs/src/plans/plans.module.ts +10 -0
- package/nestjs/src/plans/plans.service.ts +115 -0
- package/nestjs/src/platform/dto/create-tenant.dto.ts +36 -0
- package/nestjs/src/platform/dto/index.ts +2 -0
- package/nestjs/src/platform/dto/update-tenant-platform.dto.ts +44 -0
- package/nestjs/src/platform/platform.controller.ts +79 -0
- package/nestjs/src/platform/platform.module.ts +10 -0
- package/nestjs/src/platform/platform.service.ts +301 -0
- package/nestjs/src/queue/queue.module.ts +56 -0
- package/nestjs/src/redis/redis.module.ts +20 -0
- package/nestjs/src/tenants/dto/index.ts +1 -0
- package/nestjs/src/tenants/dto/update-tenant.dto.ts +15 -0
- package/nestjs/src/tenants/tenants.controller.ts +45 -0
- package/nestjs/src/tenants/tenants.module.ts +10 -0
- package/nestjs/src/tenants/tenants.service.ts +41 -0
- package/nestjs/src/tools/adapters/elevenlabs-http.adapter.ts +51 -0
- package/nestjs/src/tools/adapters/elevenlabs-ws.adapter.ts +59 -0
- package/nestjs/src/tools/handlers/calendar.tools.ts +441 -0
- package/nestjs/src/tools/handlers/notification.tools.ts +34 -0
- package/nestjs/src/tools/handlers/operator.tools.ts +303 -0
- package/nestjs/src/tools/handlers/patient.tools.ts +242 -0
- package/nestjs/src/tools/handlers/payment.tools.ts +43 -0
- package/nestjs/src/tools/handlers/validation.tools.ts +152 -0
- package/nestjs/src/tools/tool-registry.service.ts +221 -0
- package/nestjs/src/tools/tool.decorator.ts +16 -0
- package/nestjs/src/tools/tool.interfaces.ts +26 -0
- package/nestjs/src/tools/tools.module.ts +50 -0
- package/nestjs/src/treatments/dto/create-plan-item.dto.ts +27 -0
- package/nestjs/src/treatments/dto/create-treatment-plan.dto.ts +69 -0
- package/nestjs/src/treatments/dto/create-treatment.dto.ts +59 -0
- package/nestjs/src/treatments/dto/index.ts +6 -0
- package/nestjs/src/treatments/dto/update-plan-item.dto.ts +23 -0
- package/nestjs/src/treatments/dto/update-treatment-plan.dto.ts +22 -0
- package/nestjs/src/treatments/dto/update-treatment.dto.ts +70 -0
- package/nestjs/src/treatments/treatment-plans.service.ts +362 -0
- package/nestjs/src/treatments/treatments.controller.ts +265 -0
- package/nestjs/src/treatments/treatments.module.ts +14 -0
- package/nestjs/src/treatments/treatments.service.ts +165 -0
- package/nestjs/src/users/doctor-profiles.service.ts +202 -0
- package/nestjs/src/users/dto/index.ts +4 -0
- package/nestjs/src/users/dto/invite-user.dto.ts +52 -0
- package/nestjs/src/users/dto/update-clinic-assignments.dto.ts +9 -0
- package/nestjs/src/users/dto/update-doctor-profile.dto.ts +49 -0
- package/nestjs/src/users/dto/update-user.dto.ts +41 -0
- package/nestjs/src/users/users.controller.ts +142 -0
- package/nestjs/src/users/users.module.ts +11 -0
- package/nestjs/src/users/users.service.ts +250 -0
- package/nestjs/src/webhooks/elevenlabs-tool.controller.ts +66 -0
- package/nestjs/src/webhooks/elevenlabs-webhook.controller.ts +60 -0
- package/nestjs/src/webhooks/meta-webhook.controller.ts +178 -0
- package/nestjs/src/webhooks/processors/elevenlabs-webhook.processor.ts +28 -0
- package/nestjs/src/webhooks/webhooks.module.ts +17 -0
- package/nestjs/src/whatsapp/chat-context.service.ts +281 -0
- package/nestjs/src/whatsapp/dto/add-blacklist.dto.ts +13 -0
- package/nestjs/src/whatsapp/dto/index.ts +2 -0
- package/nestjs/src/whatsapp/dto/send-message.dto.ts +14 -0
- package/nestjs/src/whatsapp/listeners/meta-message.listener.ts +579 -0
- package/nestjs/src/whatsapp/meta-auth.controller.ts +268 -0
- package/nestjs/src/whatsapp/meta-instagram-auth.controller.ts +244 -0
- package/nestjs/src/whatsapp/operator.service.ts +692 -0
- package/nestjs/src/whatsapp/processors/cost-sweep.processor.ts +130 -0
- package/nestjs/src/whatsapp/processors/grace.processor.ts +191 -0
- package/nestjs/src/whatsapp/processors/message-buffer.processor.ts +138 -0
- package/nestjs/src/whatsapp/processors/message-cleanup.processor.ts +148 -0
- package/nestjs/src/whatsapp/processors/meta-token-refresh.processor.ts +114 -0
- package/nestjs/src/whatsapp/processors/operator-expiry.processor.ts +105 -0
- package/nestjs/src/whatsapp/processors/profile-update.processor.ts +234 -0
- package/nestjs/src/whatsapp/processors/session-cleanup.processor.ts +178 -0
- package/nestjs/src/whatsapp/processors/session-labels.processor.ts +248 -0
- package/nestjs/src/whatsapp/whatsapp-agent.service.ts +2506 -0
- package/nestjs/src/whatsapp/whatsapp-recovery.service.ts +117 -0
- package/nestjs/src/whatsapp/whatsapp.controller.ts +398 -0
- package/nestjs/src/whatsapp/whatsapp.module.ts +51 -0
- package/nestjs/src/whatsapp/whatsapp.service.ts +592 -0
- package/nestjs/test/app.e2e-spec.ts +25 -0
- package/nestjs/test/jest-e2e.json +9 -0
- package/nestjs/tsconfig.build.json +4 -0
- package/nestjs/tsconfig.json +25 -0
- package/nginx.example.conf +18 -0
- package/package.json +47 -0
- package/scripts/addRecipient.ts +48 -0
- package/scripts/listRecipients.ts +31 -0
- package/scripts/migrate-agent-ref.ts +86 -0
- package/scripts/migrate-sessions.ts +183 -0
- package/scripts/promote.ts +27 -0
- package/scripts/retrigger.ts +84 -0
- package/scripts/seed.ts +435 -0
- package/scripts/testSend.ts +63 -0
- package/src/app.ts +85 -0
- package/src/config/agentPrompts.json +19 -0
- package/src/config/database.ts +21 -0
- package/src/config/env.ts +94 -0
- package/src/config/index.ts +86 -0
- package/src/controllers/webhook.controller.ts +150 -0
- package/src/errors/index.ts +9 -0
- package/src/hooks/webhookValidator.ts +55 -0
- package/src/index.ts +68 -0
- package/src/migrations/001_session-architecture.ts +138 -0
- package/src/migrations/002_agent-ref.ts +65 -0
- package/src/migrations/003_shift-schedule.ts +55 -0
- package/src/migrations/004_invert-shift-to-clinic-hours.ts +30 -0
- package/src/migrations/005_composite-baileys-chat-ids.ts +60 -0
- package/src/migrations/migration.model.ts +27 -0
- package/src/migrations/migration.routes.ts +40 -0
- package/src/migrations/migration.runner.ts +112 -0
- package/src/models/ConversationClaim.ts +17 -0
- package/src/models/ConversationEvaluation.ts +91 -0
- package/src/models/Summary.ts +77 -0
- package/src/models/Transcription.ts +68 -0
- package/src/models/WhatsAppRecipient.ts +37 -0
- package/src/modules/admin/admin.routes.ts +2385 -0
- package/src/modules/admin/elevenlabs-test-chat.routes.ts +193 -0
- package/src/modules/admin/whatsapp-test-chat.routes.ts +244 -0
- package/src/modules/agents/agent.model.ts +93 -0
- package/src/modules/agents/agent.routes.ts +65 -0
- package/src/modules/appointment-validation/appointment-validation.model.ts +163 -0
- package/src/modules/appointment-validation/appointment-validation.routes.ts +275 -0
- package/src/modules/appointment-validation/appointment-validation.service.ts +1028 -0
- package/src/modules/auth/auth.model.ts +42 -0
- package/src/modules/auth/auth.routes.ts +199 -0
- package/src/modules/auth/auth.service.ts +210 -0
- package/src/modules/auth/refresh-token.model.ts +26 -0
- package/src/modules/billing/billing-alert.model.ts +28 -0
- package/src/modules/billing/billing-period-snapshot.model.ts +68 -0
- package/src/modules/billing/billing.model.ts +42 -0
- package/src/modules/billing/billing.routes.ts +67 -0
- package/src/modules/billing/billing.service.ts +562 -0
- package/src/modules/billing/payment.model.ts +42 -0
- package/src/modules/calls/call.model.ts +102 -0
- package/src/modules/calls/call.routes.ts +118 -0
- package/src/modules/campaigns/campaign.model.ts +111 -0
- package/src/modules/campaigns/campaign.routes.ts +402 -0
- package/src/modules/campaigns/campaign.service.ts +99 -0
- package/src/modules/google-calendar/google-calendar.routes.ts +31 -0
- package/src/modules/inbound-call/inbound-call-config.model.ts +49 -0
- package/src/modules/inbound-call/inbound-call.routes.ts +289 -0
- package/src/modules/leads/lead.model.ts +40 -0
- package/src/modules/leads/lead.routes.ts +246 -0
- package/src/modules/logs/log.model.ts +27 -0
- package/src/modules/logs/log.routes.ts +102 -0
- package/src/modules/plans/plan.model.ts +45 -0
- package/src/modules/surveys/survey.model.ts +70 -0
- package/src/modules/surveys/survey.routes.ts +187 -0
- package/src/modules/tenants/tenant.model.ts +181 -0
- package/src/modules/tenants/tenant.routes.ts +78 -0
- package/src/modules/users/user.routes.ts +126 -0
- package/src/modules/webhooks/elevenlabs-tool.routes.ts +94 -0
- package/src/modules/webhooks/elevenlabs-tool.service.ts +491 -0
- package/src/modules/webhooks/elevenlabs.routes.ts +34 -0
- package/src/modules/webhooks/elevenlabs.service.ts +565 -0
- package/src/modules/webhooks/unipile.routes.ts +917 -0
- package/src/modules/whatsapp/appointment.model.ts +47 -0
- package/src/modules/whatsapp/operator-request.model.ts +58 -0
- package/src/modules/whatsapp/stt-usage.model.ts +30 -0
- package/src/modules/whatsapp/whatsapp-chat.model.ts +39 -0
- package/src/modules/whatsapp/whatsapp-contact-profile.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-message.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-session.model.ts +60 -0
- package/src/modules/whatsapp/whatsapp.routes.ts +1435 -0
- package/src/plugins/cors.ts +7 -0
- package/src/plugins/jwt.ts +18 -0
- package/src/plugins/rawBody.ts +12 -0
- package/src/plugins/rbac.ts +24 -0
- package/src/routes/admin.routes.ts +208 -0
- package/src/routes/health.routes.ts +12 -0
- package/src/routes/webhook.routes.ts +12 -0
- package/src/services/ai/base.ai.ts +132 -0
- package/src/services/ai/gemini.service.ts +41 -0
- package/src/services/ai/index.ts +24 -0
- package/src/services/ai/openai.service.ts +48 -0
- package/src/services/elevenlabs.service.ts +532 -0
- package/src/services/google-calendar.service.ts +656 -0
- package/src/services/inbound-call-schedule.service.ts +174 -0
- package/src/services/netgsm.service.ts +128 -0
- package/src/services/unipile.service.ts +200 -0
- package/src/services/whatsapp-agent.service.ts +2479 -0
- package/src/services/whatsapp.service.ts +245 -0
- package/src/templates/index.ts +71 -0
- package/src/templates/receptionist.ts +44 -0
- package/src/templates/survey.ts +44 -0
- package/src/types/index.ts +218 -0
- package/src/utils/logger.ts +83 -0
- package/tsconfig.json +19 -0
- package/web/.dockerignore +4 -0
- package/web/Dockerfile +18 -0
- package/web/README.md +73 -0
- package/web/components.json +23 -0
- package/web/eslint.config.js +23 -0
- package/web/index.html +14 -0
- package/web/nginx.conf +18 -0
- package/web/package-lock.json +10292 -0
- package/web/package.json +48 -0
- package/web/public/favicon.ico +0 -0
- package/web/public/vite.svg +1 -0
- package/web/src/App.tsx +191 -0
- package/web/src/assets/react.svg +1 -0
- package/web/src/components/Layout.tsx +261 -0
- package/web/src/components/LeadConversation.tsx +251 -0
- package/web/src/components/ProtectedRoute.tsx +20 -0
- package/web/src/components/conversation-review/CardAudioPlayer.tsx +200 -0
- package/web/src/components/conversation-review/CardEvaluation.tsx +351 -0
- package/web/src/components/conversation-review/CardMetadata.tsx +44 -0
- package/web/src/components/conversation-review/ConversationCard.tsx +95 -0
- package/web/src/components/conversation-review/ReviewContainer.tsx +120 -0
- package/web/src/components/conversation-review/ReviewFilters.tsx +88 -0
- package/web/src/components/empty-state.tsx +15 -0
- package/web/src/components/page-header.tsx +19 -0
- package/web/src/components/page-loader.tsx +32 -0
- package/web/src/components/pagination.tsx +38 -0
- package/web/src/components/stat-card.tsx +27 -0
- package/web/src/components/status-badge.tsx +125 -0
- package/web/src/components/ui/alert.tsx +66 -0
- package/web/src/components/ui/badge.tsx +48 -0
- package/web/src/components/ui/button.tsx +64 -0
- package/web/src/components/ui/card.tsx +92 -0
- package/web/src/components/ui/checkbox.tsx +32 -0
- package/web/src/components/ui/dialog.tsx +158 -0
- package/web/src/components/ui/dropdown-menu.tsx +255 -0
- package/web/src/components/ui/input.tsx +21 -0
- package/web/src/components/ui/label.tsx +22 -0
- package/web/src/components/ui/progress.tsx +29 -0
- package/web/src/components/ui/select.tsx +188 -0
- package/web/src/components/ui/separator.tsx +28 -0
- package/web/src/components/ui/sheet.tsx +123 -0
- package/web/src/components/ui/skeleton.tsx +13 -0
- package/web/src/components/ui/sonner.tsx +35 -0
- package/web/src/components/ui/table.tsx +116 -0
- package/web/src/components/ui/tabs.tsx +89 -0
- package/web/src/components/ui/textarea.tsx +18 -0
- package/web/src/components/ui/tooltip.tsx +57 -0
- package/web/src/components/whatsapp/ChatDetail.tsx +417 -0
- package/web/src/components/whatsapp/ChatHeader.tsx +78 -0
- package/web/src/components/whatsapp/ChatList.tsx +107 -0
- package/web/src/components/whatsapp/ChatListItem.tsx +60 -0
- package/web/src/components/whatsapp/MessageBubble.tsx +46 -0
- package/web/src/components/whatsapp/MessageInput.tsx +63 -0
- package/web/src/components/whatsapp/MessageStream.tsx +135 -0
- package/web/src/components/whatsapp/SessionDivider.tsx +65 -0
- package/web/src/components/whatsapp/SessionHistory.tsx +119 -0
- package/web/src/components/whatsapp/settings/AiSettingsTab.tsx +268 -0
- package/web/src/components/whatsapp/settings/CalendarTab.tsx +339 -0
- package/web/src/components/whatsapp/settings/ConnectionTab.tsx +236 -0
- package/web/src/components/whatsapp/settings/NotificationsTab.tsx +109 -0
- package/web/src/components/whatsapp/settings/OperatorTab.tsx +303 -0
- package/web/src/contexts/AuthContext.tsx +103 -0
- package/web/src/index.css +130 -0
- package/web/src/lib/api.ts +92 -0
- package/web/src/lib/utils.ts +6 -0
- package/web/src/main.tsx +10 -0
- package/web/src/pages/AppointmentDetail.tsx +206 -0
- package/web/src/pages/AppointmentValidation.tsx +157 -0
- package/web/src/pages/AppointmentValidationDetail.tsx +617 -0
- package/web/src/pages/AppointmentValidationNew.tsx +1005 -0
- package/web/src/pages/Appointments.tsx +283 -0
- package/web/src/pages/Billing.tsx +126 -0
- package/web/src/pages/CallDetail.tsx +293 -0
- package/web/src/pages/CallSettings.tsx +313 -0
- package/web/src/pages/Calls.tsx +188 -0
- package/web/src/pages/CampaignDetail.tsx +216 -0
- package/web/src/pages/CampaignNew.tsx +277 -0
- package/web/src/pages/CampaignResults.tsx +185 -0
- package/web/src/pages/Campaigns.tsx +171 -0
- package/web/src/pages/Dashboard.tsx +336 -0
- package/web/src/pages/InboundSchedule.tsx +246 -0
- package/web/src/pages/LeadDetail.tsx +183 -0
- package/web/src/pages/Leads.tsx +258 -0
- package/web/src/pages/Login.tsx +99 -0
- package/web/src/pages/Register.tsx +129 -0
- package/web/src/pages/Settings.tsx +133 -0
- package/web/src/pages/SurveyDetail.tsx +232 -0
- package/web/src/pages/SurveyPreview.tsx +179 -0
- package/web/src/pages/Surveys.tsx +207 -0
- package/web/src/pages/Users.tsx +199 -0
- package/web/src/pages/WhatsApp.tsx +147 -0
- package/web/src/pages/WhatsAppSettings.tsx +215 -0
- package/web/src/pages/admin/AdminBaileysMessageLog.tsx +331 -0
- package/web/src/pages/admin/AdminBaileysRawMessages.tsx +318 -0
- package/web/src/pages/admin/AdminConversationReview.tsx +116 -0
- package/web/src/pages/admin/AdminCredits.tsx +467 -0
- package/web/src/pages/admin/AdminElevenLabsAgentDetail.tsx +332 -0
- package/web/src/pages/admin/AdminElevenLabsAgents.tsx +164 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCallDetail.tsx +214 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsConversationDetail.tsx +230 -0
- package/web/src/pages/admin/AdminElevenLabsConversations.tsx +228 -0
- package/web/src/pages/admin/AdminElevenLabsInboundCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsPhoneNumbers.tsx +506 -0
- package/web/src/pages/admin/AdminElevenLabsTestChat.tsx +258 -0
- package/web/src/pages/admin/AdminElevenLabsWebhooks.tsx +289 -0
- package/web/src/pages/admin/AdminEvaluationDashboard.tsx +520 -0
- package/web/src/pages/admin/AdminLeads.tsx +339 -0
- package/web/src/pages/admin/AdminLogs.tsx +283 -0
- package/web/src/pages/admin/AdminMigrations.tsx +247 -0
- package/web/src/pages/admin/AdminPlans.tsx +313 -0
- package/web/src/pages/admin/AdminSystem.tsx +391 -0
- package/web/src/pages/admin/AdminTenantBillableItems.tsx +464 -0
- package/web/src/pages/admin/AdminTenantDetail.tsx +1317 -0
- package/web/src/pages/admin/AdminTenants.tsx +274 -0
- package/web/src/pages/admin/AdminWhatsApp.tsx +618 -0
- package/web/src/pages/admin/AdminWhatsAppTestChat.tsx +328 -0
- package/web/src/types/index.ts +242 -0
- package/web/tsconfig.app.json +32 -0
- package/web/tsconfig.json +13 -0
- package/web/tsconfig.node.json +26 -0
- package/web/vite.config.ts +23 -0
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
# 04 - WhatsApp AI Agent
|
|
2
|
+
|
|
3
|
+
> Last updated: 2026-04-02
|
|
4
|
+
|
|
5
|
+
> Core WhatsApp AI agent flow -- the heart of the product.
|
|
6
|
+
> Covers the full lifecycle from webhook ingestion through ElevenLabs AI response delivery.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Session Lifecycle
|
|
11
|
+
|
|
12
|
+
Every patient conversation is managed through a **WhatsAppSession** record that transitions through three states:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
+-----------+
|
|
16
|
+
Incoming message | | Human replies within
|
|
17
|
+
-----------------> | waiting | -----------------------> RESOLVED (human)
|
|
18
|
+
| | cancel timer
|
|
19
|
+
+-----+-----+
|
|
20
|
+
|
|
|
21
|
+
grace period expires
|
|
22
|
+
(BullMQ delayed job)
|
|
23
|
+
|
|
|
24
|
+
v
|
|
25
|
+
+-----------+
|
|
26
|
+
| | Message debounce
|
|
27
|
+
| active | ----> AI response loop
|
|
28
|
+
| |
|
|
29
|
+
+-----+-----+
|
|
30
|
+
|
|
|
31
|
+
2h inactivity / AI timeout / manual
|
|
32
|
+
|
|
|
33
|
+
v
|
|
34
|
+
+-----------+
|
|
35
|
+
| |
|
|
36
|
+
| resolved |
|
|
37
|
+
| |
|
|
38
|
+
+-----------+
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### State Definitions
|
|
42
|
+
|
|
43
|
+
| State | Meaning | Transitions To |
|
|
44
|
+
|-------|---------|----------------|
|
|
45
|
+
| `waiting` | Contact messaged; grace timer running, waiting for human to respond first | `active` (grace expires), `resolved` (human responds) |
|
|
46
|
+
| `active` | AI has taken over the conversation; ElevenLabs WS may be connected | `resolved` (inactivity, AI timeout, manual, human takeover) |
|
|
47
|
+
| `resolved` | Conversation ended | Next incoming message creates a new session |
|
|
48
|
+
|
|
49
|
+
### Resolution Types
|
|
50
|
+
|
|
51
|
+
| `resolved_by` | Trigger |
|
|
52
|
+
|----------------|---------|
|
|
53
|
+
| `human` | A real human (not AI) sent a message during `waiting` state |
|
|
54
|
+
| `ai_timeout` | ElevenLabs conversation ended or AI did not respond |
|
|
55
|
+
| `timeout` | 2h inactivity sweep resolved the session |
|
|
56
|
+
| `manual` | Operator clicked "resolve" in the UI, or grace expired with no agent configured |
|
|
57
|
+
|
|
58
|
+
### Grace Period
|
|
59
|
+
|
|
60
|
+
- **Default:** 180 seconds (configurable per clinic via `clinic_grace_period_seconds` -- read directly from clinic, no tenant fallback)
|
|
61
|
+
- **Current implementation:** `setTimeout` in memory
|
|
62
|
+
- **Target implementation:** BullMQ delayed job on `whatsapp:grace` queue
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Target: BullMQ delayed job
|
|
66
|
+
await graceQueue.add('grace-expired', { sessionId, chatId, tenantId }, {
|
|
67
|
+
delay: gracePeriodSeconds * 1000,
|
|
68
|
+
jobId: `grace:${sessionId}`, // Deduplicate -- same session replaces the job
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
On grace expiry:
|
|
73
|
+
1. Verify session is still `waiting` (may have been resolved by a human in the meantime)
|
|
74
|
+
2. Check if any human has ever messaged in this chat -- if so, resolve as `manual`
|
|
75
|
+
3. Check AI shift schedule -- if outside AI hours, resolve as `manual`
|
|
76
|
+
4. If agent is configured: transition to `active`, replay all pending contact messages to AI
|
|
77
|
+
5. If no agent: resolve as `manual`
|
|
78
|
+
|
|
79
|
+
### Message Debouncing
|
|
80
|
+
|
|
81
|
+
When the session is `active` and a new contact message arrives, the system does not send it to AI immediately. Instead:
|
|
82
|
+
|
|
83
|
+
1. Cancel any existing buffer timer for this session
|
|
84
|
+
2. Set a new timer with a **random delay between 10s and 50s**
|
|
85
|
+
3. When the timer fires, collect all unanswered contact messages since the last AI response
|
|
86
|
+
4. Combine them into a single text payload and send to ElevenLabs
|
|
87
|
+
|
|
88
|
+
**Current:** `setTimeout` in memory (`messageBufferTimers` Map)
|
|
89
|
+
**Target:** BullMQ delayed job on `whatsapp:message-buffer` queue
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
await messageBufferQueue.add('buffer-expired', { sessionId, chatId }, {
|
|
93
|
+
delay: randomDelay(10_000, 50_000),
|
|
94
|
+
jobId: `buffer:${sessionId}`, // Replace existing job for same session
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 2-Hour Inactivity Auto-Resolve
|
|
99
|
+
|
|
100
|
+
A repeatable sweep runs every 5 minutes (BullMQ `whatsapp:session-cleanup`). It finds `active` sessions where `updatedAt < now - 2h` and `awaiting_operator != true`, then:
|
|
101
|
+
|
|
102
|
+
1. Closes the ElevenLabs WS connection if still open
|
|
103
|
+
2. Sets status to `resolved`, `resolved_by: 'timeout'`
|
|
104
|
+
3. Unlinks from chat (`active_session_id = null`)
|
|
105
|
+
4. Fires profile summary update and session labeling (fire-and-forget)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 2. Message Processing Pipeline
|
|
110
|
+
|
|
111
|
+
Complete step-by-step flow from webhook to AI response:
|
|
112
|
+
|
|
113
|
+
### Step 1: Webhook Receives Message
|
|
114
|
+
|
|
115
|
+
The webhook endpoint (`POST /webhooks/meta`) receives a payload from the Meta Business API. The payload is normalized to a common internal format.
|
|
116
|
+
|
|
117
|
+
Key payload fields:
|
|
118
|
+
- `account_id` -- identifies the clinic's Meta WhatsApp Business Account
|
|
119
|
+
- `chat_id` -- identifies the specific conversation (Meta conversation ID)
|
|
120
|
+
- `message` -- text content
|
|
121
|
+
- `content_type` -- media type (e.g., `imageMessage`, `audioMessage`)
|
|
122
|
+
- `is_sender` -- `true` if the message was sent by the clinic (echo)
|
|
123
|
+
- `attendees[]` -- contact info (phone, name)
|
|
124
|
+
- `ad_context` -- ad attribution if the patient clicked an ad to start the chat
|
|
125
|
+
- `quoted_text` -- text of a quoted (replied-to) message
|
|
126
|
+
|
|
127
|
+
### Step 2: System Message Filtering
|
|
128
|
+
|
|
129
|
+
Over 40 regex patterns detect non-conversational messages in both Turkish and English:
|
|
130
|
+
|
|
131
|
+
- Voice/video call notifications
|
|
132
|
+
- Message deletions
|
|
133
|
+
- End-to-end encryption notices
|
|
134
|
+
- Disappearing messages toggles
|
|
135
|
+
- Live location sharing
|
|
136
|
+
- Payment notifications
|
|
137
|
+
- Group management events
|
|
138
|
+
- View-once message placeholders
|
|
139
|
+
- Old WhatsApp version warnings
|
|
140
|
+
|
|
141
|
+
System messages are **stored for visibility** but never trigger sessions or AI. Detection uses two layers:
|
|
142
|
+
1. Structural fields: `is_event === true` or `hidden === true`
|
|
143
|
+
2. Text pattern fallback: regex matching against the 40+ patterns
|
|
144
|
+
|
|
145
|
+
### Step 3: Voice Transcription (STT)
|
|
146
|
+
|
|
147
|
+
If `content_type === 'audioMessage'` and the tenant has `voice_transcription` enabled:
|
|
148
|
+
|
|
149
|
+
1. Download audio via Meta Business API (`downloadMedia`)
|
|
150
|
+
2. Send to ElevenLabs Scribe v1 STT API
|
|
151
|
+
3. Replace `payload.message` with the transcript text
|
|
152
|
+
4. Set `content_type = 'transcribedAudio'`
|
|
153
|
+
5. Log STT usage for billing
|
|
154
|
+
|
|
155
|
+
If transcription fails, the original media label is preserved as fallback.
|
|
156
|
+
|
|
157
|
+
### Step 4: Operator Reply Detection
|
|
158
|
+
|
|
159
|
+
Before processing as a regular message, check if this is a reply from a known operator (doctor):
|
|
160
|
+
|
|
161
|
+
1. Collect all known operator phones from tenant settings (`operator_phone`, `conversation_forwarding_phone`)
|
|
162
|
+
2. Search for REF code pattern (`/\b([A-Z]-[A-Z0-9]{4})\b/`) in both the message text and `quoted_text`
|
|
163
|
+
3. If REF code found and sender matches an operator phone: route to `handleOperatorReply()` (see 05-OPERATOR-WORKFLOW.md)
|
|
164
|
+
4. If REF code found and `is_sender === true`: verify the REF code exists as a pending request (operator using the same device)
|
|
165
|
+
|
|
166
|
+
### Step 5: Patient Find-or-Create
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// Find existing patient by phone within the clinic, or create a new one
|
|
170
|
+
const patient = await patientService.findOrCreate({
|
|
171
|
+
clinic_id: clinic.clinic_id,
|
|
172
|
+
phone: contactPhone,
|
|
173
|
+
display_name: contactName,
|
|
174
|
+
});
|
|
175
|
+
// Updates patient_last_seen_at on every message
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Creates a `Patient` record on first contact; updates `patient_display_name` and `patient_last_seen_at` on every message. The AI summary is stored on `patient.patient_ai_summary` (encrypted).
|
|
179
|
+
|
|
180
|
+
### Step 6: Chat Find/Create
|
|
181
|
+
|
|
182
|
+
1. Look up `WhatsAppChat` by `chat_external_id` (Meta conversation ID)
|
|
183
|
+
2. If not found, try matching by `clinic_id + contact_phone`
|
|
184
|
+
3. If still not found, create a new chat record with `chat_patient_id` linking to the patient
|
|
185
|
+
|
|
186
|
+
### Step 7: Session Find/Create
|
|
187
|
+
|
|
188
|
+
1. Check `chat.active_session_id` for an existing non-resolved session
|
|
189
|
+
2. If session is `waiting`: store the message (timer already running), return
|
|
190
|
+
3. If session is `active`: forward to `handleIncomingMessage()` for debouncing
|
|
191
|
+
4. If no active session: create a new session in `waiting` state with grace deadline
|
|
192
|
+
|
|
193
|
+
### Step 8: Grace Period or AI Trigger
|
|
194
|
+
|
|
195
|
+
- **New session (waiting):** Start grace period timer. If `fixed_first_message_enabled`, send the configured first message via Meta Business API.
|
|
196
|
+
- **Existing active session:** Start/reset message buffer timer (debounce).
|
|
197
|
+
|
|
198
|
+
### Step 9: Message Buffering
|
|
199
|
+
|
|
200
|
+
When the buffer timer fires:
|
|
201
|
+
1. Find the last AI response in this session
|
|
202
|
+
2. Collect all contact messages after that AI response
|
|
203
|
+
3. Combine into a single text payload (`\n`-separated)
|
|
204
|
+
4. Pass to `triggerAiResponse()`
|
|
205
|
+
|
|
206
|
+
### Step 10: ElevenLabs WebSocket Connection
|
|
207
|
+
|
|
208
|
+
If no live connection exists for this chat:
|
|
209
|
+
1. Build `user_summary` from patient record (name, preferred language, notes, AI summary, tags)
|
|
210
|
+
2. If reconnecting and `chat_continuity` is enabled: build `continuation_directive` with full chat history
|
|
211
|
+
3. Get a signed URL from ElevenLabs API
|
|
212
|
+
4. Open WebSocket: `new WebSocket(signedUrl + '?textOnly=true')`
|
|
213
|
+
5. On open: send `conversation_initiation_client_data` with dynamic variables
|
|
214
|
+
6. Wait for `conversation_initiation_metadata` (15s timeout)
|
|
215
|
+
7. Store conversation ID on the session record
|
|
216
|
+
8. Start keepalive ping timer (30s interval)
|
|
217
|
+
9. Send chat context via `contextual_update`
|
|
218
|
+
|
|
219
|
+
If connection already exists and is alive: reuse it.
|
|
220
|
+
|
|
221
|
+
### Step 11: AI Response -> Send via Meta API
|
|
222
|
+
|
|
223
|
+
When ElevenLabs sends `agent_response`:
|
|
224
|
+
1. Extract response text
|
|
225
|
+
2. Send via Meta Business API to the patient's WhatsApp
|
|
226
|
+
3. Store as `WhatsAppMessage` with `sender: 'human', sender_name: 'AI'`
|
|
227
|
+
4. Message text (`msg_text`) is encrypted at rest using AES-256-GCM via `MESSAGE_ENCRYPTION_KEY`
|
|
228
|
+
5. Increment chat and session message counts
|
|
229
|
+
|
|
230
|
+
**Message encryption:** All stored messages have `msg_text` and `msg_quoted_text` encrypted with AES-256-GCM. Decryption happens on read. Messages are auto-deleted after `clinic.clinic_message_retention_days` (default 7 days) via a BullMQ scheduled job.
|
|
231
|
+
|
|
232
|
+
### Step 12: Deduplication on Echo
|
|
233
|
+
|
|
234
|
+
When Meta delivers our outgoing message back via webhook, it arrives as `is_sender === true`. Deduplication runs in two layers:
|
|
235
|
+
|
|
236
|
+
1. **Primary:** Match by `msg_external_id` (Meta message ID) -- if already stored, skip
|
|
237
|
+
2. **Fallback:** Match by `chat_id + text + sent_via_api + createdAt > 1min ago` -- covers cases where the API did not return a message ID at send time
|
|
238
|
+
|
|
239
|
+
If a match is found, update the stored message with the real message ID (for future dedup) and skip processing.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 3. Complete Sequence Diagram
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
Patient Meta Business API Webhook Handler WhatsAppAgentService ElevenLabs
|
|
247
|
+
| | | | |
|
|
248
|
+
|--- WA message --->| | | |
|
|
249
|
+
| |--- HTTP POST ----->| | |
|
|
250
|
+
| | | | |
|
|
251
|
+
| | | 1. Validate secret | |
|
|
252
|
+
| | | 2. Filter system msg | |
|
|
253
|
+
| | | 3. STT (if audio) | |
|
|
254
|
+
| | | 4. Detect operator | |
|
|
255
|
+
| | | 5. Find/create patient| |
|
|
256
|
+
| | | 6. Find/create chat | |
|
|
257
|
+
| | | 7. Find/create sess. | |
|
|
258
|
+
| | | | |
|
|
259
|
+
| | |--- startSession() --->| |
|
|
260
|
+
| | | (if new session) | |
|
|
261
|
+
| | | | |
|
|
262
|
+
| | | |-- BullMQ: grace ----->|
|
|
263
|
+
| | | | (delayed 180s) |
|
|
264
|
+
| | | | |
|
|
265
|
+
| ... 180s pass, no human reply ... | |
|
|
266
|
+
| | | | |
|
|
267
|
+
| | | |<- grace expired |
|
|
268
|
+
| | | | |
|
|
269
|
+
| | | | Session -> active |
|
|
270
|
+
| | | | Replay pending msgs |
|
|
271
|
+
| | | | |
|
|
272
|
+
| | | |-- getSignedUrl() ---->|
|
|
273
|
+
| | | |<-- signedUrl ---------|
|
|
274
|
+
| | | | |
|
|
275
|
+
| | | |== WS connect ========>|
|
|
276
|
+
| | | |-- init_client_data -->|
|
|
277
|
+
| | | |<- init_metadata ------|
|
|
278
|
+
| | | | |
|
|
279
|
+
| | | |-- contextual_update ->|
|
|
280
|
+
| | | |-- user_message ------>|
|
|
281
|
+
| | | | |
|
|
282
|
+
| | | |<- agent_response -----|
|
|
283
|
+
| | | | |
|
|
284
|
+
| | | |-- sendMessage() --+ |
|
|
285
|
+
| |<------- Meta API send ---------------------+ | |
|
|
286
|
+
|<-- WA message ----| | | | |
|
|
287
|
+
| | | | Store AI msg | |
|
|
288
|
+
| | | | (encrypted) | |
|
|
289
|
+
| | | | |
|
|
290
|
+
|--- next message ->|--- HTTP POST ----->| | |
|
|
291
|
+
| | |--- handleIncoming() ->| |
|
|
292
|
+
| | | | |
|
|
293
|
+
| | | |-- BullMQ: buffer ---->|
|
|
294
|
+
| | | | (10-50s random) |
|
|
295
|
+
| | | | |
|
|
296
|
+
| ... buffer timer fires ... | |
|
|
297
|
+
| | | | |
|
|
298
|
+
| | | |-- user_message ------>|
|
|
299
|
+
| | | | (combined msgs) |
|
|
300
|
+
| | | | |
|
|
301
|
+
| | | |<- client_tool_call ---|
|
|
302
|
+
| | | | (e.g., reserve_slot)|
|
|
303
|
+
| | | | |
|
|
304
|
+
| | | |-- ToolRegistry ------>|
|
|
305
|
+
| | | |<- tool result --------|
|
|
306
|
+
| | | | |
|
|
307
|
+
| | | |-- client_tool_result >|
|
|
308
|
+
| | | |<- agent_response -----|
|
|
309
|
+
| | | | |
|
|
310
|
+
| |<------- Meta API send ---------------------+ |
|
|
311
|
+
|<-- WA message ----| | | |
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 4. ElevenLabs WebSocket Protocol
|
|
317
|
+
|
|
318
|
+
### Connection Lifecycle
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
1. GET signed URL: elevenlabsService.getSignedUrl({ agentId })
|
|
322
|
+
2. Connect: new WebSocket(`${signedUrl}?textOnly=true`)
|
|
323
|
+
3. On WS open: send conversation_initiation_client_data
|
|
324
|
+
4. Wait for: conversation_initiation_metadata (15s timeout)
|
|
325
|
+
5. Active: exchange user_message / agent_response / client_tool_call / client_tool_result
|
|
326
|
+
6. Keepalive: send ping + user_activity every 30s
|
|
327
|
+
7. Close: on session resolve, 2h idle cleanup, or ElevenLabs-initiated close
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Message Types
|
|
331
|
+
|
|
332
|
+
| Type | Direction | Payload | Purpose |
|
|
333
|
+
|------|-----------|---------|---------|
|
|
334
|
+
| `conversation_initiation_client_data` | Client -> EL | `{ conversation_config_override, dynamic_variables }` | Initialize conversation with agent config overrides and dynamic variables |
|
|
335
|
+
| `conversation_initiation_metadata` | EL -> Client | `{ conversation_id }` | Confirmation that conversation is ready; provides the conversation ID |
|
|
336
|
+
| `user_message` | Client -> EL | `{ text }` | Send patient's message (or combined buffered messages) to the AI |
|
|
337
|
+
| `contextual_update` | Client -> EL | `{ text }` | Send background context (chat history, operator responses) without triggering a response |
|
|
338
|
+
| `agent_response` | EL -> Client | `{ agent_response }` | AI-generated response text to send to the patient |
|
|
339
|
+
| `client_tool_call` | EL -> Client | `{ tool_call_id, tool_name, parameters }` | AI requests a tool execution (check_availability, reserve_slot, etc.) |
|
|
340
|
+
| `client_tool_result` | Client -> EL | `{ tool_call_id, result, is_error }` | Return tool execution result to AI. **CRITICAL:** `type` must be lowercase, `is_error` is required |
|
|
341
|
+
| `ping` / `pong` | Both | `{ event_id }` | ElevenLabs sends `ping`, client responds with `pong` carrying the same `event_id` |
|
|
342
|
+
| `user_activity` | Client -> EL | `{}` | Keepalive signal to reset ElevenLabs' inactivity timer |
|
|
343
|
+
|
|
344
|
+
### Dynamic Variables
|
|
345
|
+
|
|
346
|
+
Sent in `conversation_initiation_client_data.dynamic_variables`:
|
|
347
|
+
|
|
348
|
+
| Variable | Value | Source |
|
|
349
|
+
|----------|-------|--------|
|
|
350
|
+
| `user_summary` | Patient profile summary (name, language, notes, AI history summary, tags) or default "Ilk defa iletisime gecen hasta" | `Patient` record |
|
|
351
|
+
| `current_date` | `YYYY-MM-DD` format | `new Date().toISOString().split('T')[0]` |
|
|
352
|
+
| `continuation_directive` | Full chat history + "don't re-introduce yourself" instructions | Built on WS reconnection when `chat_continuity` is enabled |
|
|
353
|
+
|
|
354
|
+
### Keepalive
|
|
355
|
+
|
|
356
|
+
- **Interval:** 30 seconds (`WS_PING_INTERVAL_MS`)
|
|
357
|
+
- **Action:** TCP-level `ws.ping()` + ElevenLabs-level `{ type: 'user_activity' }` message
|
|
358
|
+
- Prevents ElevenLabs from closing the connection due to inactivity
|
|
359
|
+
|
|
360
|
+
### Handshake Timeout
|
|
361
|
+
|
|
362
|
+
- **15 seconds** to receive `conversation_initiation_metadata` after sending `conversation_initiation_client_data`
|
|
363
|
+
- On timeout: close WebSocket, reject the connection promise, log error
|
|
364
|
+
- Next incoming message will trigger a fresh connection attempt (lazy reconnect)
|
|
365
|
+
|
|
366
|
+
### Chat Context Building
|
|
367
|
+
|
|
368
|
+
The `buildChatContext()` method constructs a timeline string sent via `contextual_update`:
|
|
369
|
+
|
|
370
|
+
1. Fetch all `WhatsAppMessage` records for the current session
|
|
371
|
+
2. Fetch all `OperatorRequest` records for the current session
|
|
372
|
+
3. Build timeline entries:
|
|
373
|
+
- Messages: `"Patient: {text}"` or `"Operator: {text}"`
|
|
374
|
+
- Operator request events: `"[SISTEM: Hastanin {N} medyasi doktora iletildi -- Ref: {code}]"`
|
|
375
|
+
- Operator responses: `"[SISTEM: Doktor yanit verdi -- Ref: {code}: "{response}"]"`
|
|
376
|
+
- Operator expiry: `"[SISTEM: Doktordan yanit alinamadi -- Ref: {code} suresi doldu]"`
|
|
377
|
+
4. Sort by timestamp
|
|
378
|
+
5. Prepend ad context if the first contact message has `ad_context` (ad attribution)
|
|
379
|
+
6. Format: `"Current conversation:\n{timeline}"`
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## 5. Chat Continuity (WS Reconnection)
|
|
384
|
+
|
|
385
|
+
When an ElevenLabs WebSocket connection drops (ElevenLabs timeout, network issue, process restart), the system reconnects lazily on the next message. If `chat_continuity` is enabled for the tenant:
|
|
386
|
+
|
|
387
|
+
### Flow
|
|
388
|
+
|
|
389
|
+
1. `triggerAiResponse()` detects no live connection
|
|
390
|
+
2. Check if `session.elevenlabs_conversation_id` exists (indicates a reconnection)
|
|
391
|
+
3. If `chat_continuity` feature flag is enabled:
|
|
392
|
+
- Build full chat history via `buildChatContext()`
|
|
393
|
+
- Find the last AI message in the session
|
|
394
|
+
- Construct `continuation_directive`:
|
|
395
|
+
```
|
|
396
|
+
ONEMLI -- KONUSMA DEVAMI:
|
|
397
|
+
Bu hasta ile onceden baslamis bir konusma var. Teknik bir baglanti yenilenmesi yasandi.
|
|
398
|
+
Kendini TANITMA. Selam VERME. Hosgeldin DEME. Konusmaya kaldigin yerden DEVAM ET.
|
|
399
|
+
En son gonderdigin mesaj: "{last AI message text}"
|
|
400
|
+
|
|
401
|
+
Current conversation:
|
|
402
|
+
{full timeline}
|
|
403
|
+
```
|
|
404
|
+
4. `continuation_directive` is sent as a dynamic variable at connection time
|
|
405
|
+
5. `contextual_update` with chat history is sent as backup after connection is ready
|
|
406
|
+
6. Previous conversation ID is pushed to `elevenlabs_previous_conversation_ids` array
|
|
407
|
+
|
|
408
|
+
### Requirements
|
|
409
|
+
|
|
410
|
+
- Tenant must have `chat_continuity` in `enabled_features`
|
|
411
|
+
- Agent prompt on ElevenLabs dashboard must include `{{continuation_directive}}` placeholder
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## 6. AI Shift Scheduling
|
|
416
|
+
|
|
417
|
+
The AI agent is **active OUTSIDE clinic working hours**. The schedule defines when humans handle messages; AI fills in the gaps.
|
|
418
|
+
|
|
419
|
+
### Logic
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
Schedule entries = clinic working hours (humans active)
|
|
423
|
+
AI active = NOT within any scheduled window for the current day
|
|
424
|
+
|
|
425
|
+
If ai_shift_enabled == false --> AI active 24/7
|
|
426
|
+
If no entry for current day --> AI active all day
|
|
427
|
+
If current time within entry --> AI inactive (clinic is open)
|
|
428
|
+
If current time outside entry --> AI active
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Schedule Format
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Per-day schedule stored in clinic_ai_shift_schedule (JSONB)
|
|
435
|
+
interface AiShiftEntry {
|
|
436
|
+
day: number; // 0=Sunday, 1=Monday, ..., 6=Saturday
|
|
437
|
+
start: string; // "09:00" (HH:mm)
|
|
438
|
+
end: string; // "18:00" (HH:mm)
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Timezone
|
|
443
|
+
|
|
444
|
+
All schedule calculations use `Europe/Istanbul` timezone. The system converts the current UTC time to Istanbul time before comparing against schedule windows. Overnight schedules (e.g., `22:00` to `06:00`) are handled correctly.
|
|
445
|
+
|
|
446
|
+
### Where AI Shift Is Checked
|
|
447
|
+
|
|
448
|
+
AI shift is checked at three points:
|
|
449
|
+
1. **Grace period expiry** -- if outside AI hours, session resolves as `manual` instead of activating AI
|
|
450
|
+
2. **Message buffer expiry** -- if outside AI hours, buffered messages are not sent to AI
|
|
451
|
+
3. **Replay pending messages** -- if outside AI hours, pending messages are not replayed
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## 7. Redis State (Target Architecture)
|
|
456
|
+
|
|
457
|
+
All in-memory Maps are replaced by Redis hashes and BullMQ jobs:
|
|
458
|
+
|
|
459
|
+
### WebSocket Connection State
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
Key: ws:conn:{chatId}
|
|
463
|
+
Type: Hash
|
|
464
|
+
TTL: 3 hours (10800s)
|
|
465
|
+
Fields:
|
|
466
|
+
agentId string ElevenLabs agent ID
|
|
467
|
+
sessionId string WhatsApp session ID
|
|
468
|
+
tenantId string Tenant ID
|
|
469
|
+
conversationId string ElevenLabs conversation ID
|
|
470
|
+
ready '1'|'0' Whether handshake completed
|
|
471
|
+
lastActivity ISO date Last message activity timestamp
|
|
472
|
+
signedUrl string For reconnecting WebSocket on process restart
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Important:** The actual WebSocket object (`ws`) cannot be stored in Redis. It lives in the NestJS process. Redis stores metadata so the system knows which connections exist. On process restart, connections are re-established from stored signed URLs.
|
|
476
|
+
|
|
477
|
+
### Timer Replacement
|
|
478
|
+
|
|
479
|
+
| Current (in-memory) | Target (BullMQ) | Queue | Job ID Pattern |
|
|
480
|
+
|---------------------|------------------|-------|----------------|
|
|
481
|
+
| `sessionTimers: Map<sessionId, Timeout>` | Delayed job | `whatsapp:grace` | `grace:{sessionId}` |
|
|
482
|
+
| `messageBufferTimers: Map<sessionId, Timeout>` | Delayed job | `whatsapp:message-buffer` | `buffer:{sessionId}` |
|
|
483
|
+
| `setInterval(() => sweepInactiveSessions(), 5m)` | Repeatable job | `whatsapp:session-cleanup` | Every 300,000ms |
|
|
484
|
+
| `setInterval(() => sweepConversationCosts(), 10m)` | Repeatable job | `whatsapp:cost-sweep` | Every 600,000ms |
|
|
485
|
+
| `setInterval(() => sweepExpiredOperatorRequests(), 15m)` | Repeatable job | `whatsapp:operator-expiry` | Every 900,000ms |
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## 8. Patient Profile Management
|
|
490
|
+
|
|
491
|
+
### Auto-Creation
|
|
492
|
+
|
|
493
|
+
On first contact, a `Patient` record is created (find-or-create by `clinic_id + phone`) with:
|
|
494
|
+
- `patient_phone`, `patient_display_name`, `patient_first_seen_at`, `patient_last_seen_at`
|
|
495
|
+
- Empty: `patient_notes`, `patient_tags`, `patient_ai_summary`
|
|
496
|
+
|
|
497
|
+
### AI Summary Update (Post-Session)
|
|
498
|
+
|
|
499
|
+
After a session resolves (any `resolved_by`), if the session had 2+ messages:
|
|
500
|
+
|
|
501
|
+
1. Build a timestamped transcript from session messages
|
|
502
|
+
2. Send to the configured AI provider with a detailed system prompt:
|
|
503
|
+
- Only extract information from `[HASTA]` (patient) lines
|
|
504
|
+
- Convert relative dates to absolute dates
|
|
505
|
+
- Structure as bullet points: personal info, health, appointments, communication preferences, preferred language, important notes
|
|
506
|
+
- Preserve existing summary data not contradicted by new conversation
|
|
507
|
+
3. Extract `PREFERRED_LANGUAGE` from AI response and store separately
|
|
508
|
+
4. Update `patient.patient_ai_summary` (encrypted) and `patient.patient_preferred_language`
|
|
509
|
+
|
|
510
|
+
This is fire-and-forget -- errors are logged but never block session resolution.
|
|
511
|
+
|
|
512
|
+
### Session Labeling (Post-Session)
|
|
513
|
+
|
|
514
|
+
After a session resolves, an AI call generates:
|
|
515
|
+
- `sentiment`: `positive` | `neutral` | `negative`
|
|
516
|
+
- `categories`: e.g., `['appointment_inquiry']`, `['callback_required']`, `['other']`
|
|
517
|
+
- `llm_summary`: 2-3 sentence summary of the patient's request and outcome
|
|
518
|
+
- Creates a `Lead` record linked to the session
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## 9. Client Tools (AI Tool Calls)
|
|
523
|
+
|
|
524
|
+
When ElevenLabs sends a `client_tool_call` message, the system dispatches to the appropriate handler:
|
|
525
|
+
|
|
526
|
+
| Tool | Description | Feature Flag |
|
|
527
|
+
|------|-------------|-------------|
|
|
528
|
+
| `forward_to_operator` | Forward patient media to doctor (see 05-OPERATOR-WORKFLOW.md) | `operator_workflow` |
|
|
529
|
+
| `forward_conversation_to_operator` | Forward conversation text to doctor | `conversation_forwarding` |
|
|
530
|
+
| `send_example_photo` | Send pre-configured guide photo to patient | `operator_workflow` |
|
|
531
|
+
| `check_availability` | Check free appointment slots for a date | `google_calendar` |
|
|
532
|
+
| `reserve_slot` | Book an appointment | `google_calendar` |
|
|
533
|
+
| `list_reservations` | List patient's appointments | `google_calendar` |
|
|
534
|
+
| `cancel_reservation` | Cancel an appointment | `google_calendar` |
|
|
535
|
+
| `reschedule_reservation` | Reschedule an appointment | `google_calendar` |
|
|
536
|
+
| `check_patient_history` | Get patient profile and appointment history | -- |
|
|
537
|
+
| `call_patient` | Initiate outbound voice call to patient | -- |
|
|
538
|
+
|
|
539
|
+
**Tool result format (critical ElevenLabs requirement):**
|
|
540
|
+
```json
|
|
541
|
+
{
|
|
542
|
+
"type": "client_tool_result",
|
|
543
|
+
"tool_call_id": "xxx",
|
|
544
|
+
"result": "JSON string",
|
|
545
|
+
"is_error": false
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
- `type` **must** be lowercase (`client_tool_result`, not `CLIENT_TOOL_RESULT`)
|
|
550
|
+
- `is_error` is **required** -- omitting it causes WS 1008 disconnect
|
|
551
|
+
|
|
552
|
+
### Target Architecture: Generalized Tool Registry
|
|
553
|
+
|
|
554
|
+
In the NestJS rewrite, tools are extracted from the monolithic `WhatsAppAgentService` into a generalized `ToolRegistryService` with `@Tool()` decorators. See Section 16 of the Migration Reference for the full design. Channel-specific adapters (`ElevenLabsWsAdapter`, `ElevenLabsHttpAdapter`) translate between AI provider protocols and the generic `ToolContext + ToolResult` interface.
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## 10. Self-Sent Message Handling
|
|
559
|
+
|
|
560
|
+
When `is_sender === true` (message sent from the clinic's WhatsApp):
|
|
561
|
+
|
|
562
|
+
1. **Deduplication:** Check if this message was sent via our API (Meta echo). Match by `msg_external_id` or by `text + sent_via_api + recent timestamp`.
|
|
563
|
+
2. **Waiting session:** If the current session is `waiting`, resolve it as `human` -- a real person responded before the grace period expired.
|
|
564
|
+
3. **Active AI session:** If the session is `active` with no human takeover, auto-switch to human mode (close ElevenLabs WS).
|
|
565
|
+
4. **Store:** Save the message as `sender: 'human', sender_name: 'WhatsApp'`.
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## 11. Fixed First Message
|
|
570
|
+
|
|
571
|
+
If `fixed_first_message_enabled` is true on the clinic and a `fixed_first_message` string is configured, the system sends this message to the patient immediately when a new session is created (before the grace period even starts). This is typically a greeting or clinic hours notice.
|
|
572
|
+
|
|
573
|
+
The message is sent fire-and-forget via Meta Business API and stored as `sender: 'system', sender_name: 'AI'`.
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## 12. Background Sweeps Summary
|
|
578
|
+
|
|
579
|
+
| Sweep | Interval | Queue | Purpose |
|
|
580
|
+
|-------|----------|-------|---------|
|
|
581
|
+
| Stuck session sweep | 5 min | `whatsapp:session-cleanup` | Find `waiting` sessions past their `grace_deadline` with no timer, trigger grace expiry |
|
|
582
|
+
| Inactive session sweep | 5 min | `whatsapp:session-cleanup` | Resolve `active` sessions with no activity for 2h |
|
|
583
|
+
| Conversation cost sweep | 10 min | `whatsapp:cost-sweep` | Fetch ElevenLabs conversation cost for resolved sessions (up to 20 per sweep) |
|
|
584
|
+
| Operator request expiry | 15 min | `whatsapp:operator-expiry` | Expire `pending` operator requests past timeout, notify AI |
|
|
585
|
+
| Idle connection cleanup | 1 min | (inline) | Close ElevenLabs WS connections with no activity for 2h |
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## 13. Error Handling and Recovery
|
|
590
|
+
|
|
591
|
+
### Session Recovery on Startup
|
|
592
|
+
|
|
593
|
+
On process start, the system queries all `waiting` sessions from the database:
|
|
594
|
+
- If `grace_deadline` has passed: trigger grace expiry immediately
|
|
595
|
+
- If `grace_deadline` is in the future: set a timer for the remaining time
|
|
596
|
+
|
|
597
|
+
### Lazy WS Reconnection
|
|
598
|
+
|
|
599
|
+
When `triggerAiResponse()` finds no live connection:
|
|
600
|
+
1. Clean up the stale connection entry
|
|
601
|
+
2. Create a fresh connection with `isReconnect: true` if the session already had a conversation
|
|
602
|
+
3. The new connection gets full chat context via `contextual_update`
|
|
603
|
+
|
|
604
|
+
No eager reconnection is attempted on WS close. The system reconnects lazily on the next incoming message.
|
|
605
|
+
|
|
606
|
+
### Blacklisted Numbers
|
|
607
|
+
|
|
608
|
+
Numbers in `clinic_blacklisted_numbers` are processed (messages stored) but never trigger sessions or AI responses.
|