- Reset conversation stage to a fresh start on every new voice call
(recall and reminder alike), and on every hangup/timeout/goodbye,
so an abandoned or completed call never leaves a patient stuck
mid-flow for their next call or text.
- Add a staleness check to the SMS flow: after a long idle gap,
check whether the patient's reply still reads as a continuation
before resuming the stored stage, falling back to a friendly
"it's been a while" re-greeting when unsure.
- Fix parseTime() to handle Twilio's speech-to-text output, which
renders "pm"/"am" with periods and often drops the colon in a
spoken time (e.g. "100 p.m." -> 1:00 PM) — this was silently
failing and causing the AI to loop asking for a time it had
already been given.
- Extend the voice Gather timeout so the AI doesn't hang up on a
patient who just needs a few seconds to respond.
- Reword the recall-call opening line and rework the post-"yes"
prompt to ask for day and time together.
- Fix rclone WebDAV server to serve only the backups folder instead
of the entire backend app directory (was exposing .env and source).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a new recall conversation flow (recall-graph.ts) triggered from the
Patient Connection "AI Call" button: offers an existing patient a routine
exam & cleaning, classifies their yes/no reply, then books directly onto
the schedule (Column A, 30-minute slots, real conflict checking against
office hours and existing appointments) instead of only capturing a
preference for staff to follow up on.
Also adds a "Voice call" label in the message thread to distinguish call
transcripts from SMS, and updates the Call Templates settings UI with an
editable recall greeting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AI phone calls now route through the same routePatientTurn() rule engine
as SMS (office-hours checks, slot-conflict checks, MassHealth eligibility,
staff escalation) instead of a freeform LLM chat, and speak in the
patient's preferred language (English/Spanish). Adds a separate "Call
Templates" settings section so spoken wording can be customized
independently from SMS wording.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an "AI Call" option alongside Call/SMS/Chat that places an outbound
call where Lisa converses live with the patient via Twilio speech
gather + TTS, using the same conversational AI as chat. Includes a
separate, user-editable Call Template (Settings > AI Chat/Call Settings)
so the call greeting can be customized independently of SMS templates.
Also fixes the outbound-call webhook URL to always use the fixed public
Twilio hostname (CLOUDFLARE_HOST) instead of deriving it from the
triggering request, since staff-browser requests arrive over the
LAN-only hostname, which Twilio can never reach.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Vite resolves .tsx before .jsx (per extensions order in vite.config.ts),
so .jsx files were never loaded at runtime. Switching to TSX-only source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
selenium-settings-card.jsx didn't exist and settings-page.jsx was missing
the import and case "selenium" handler, so the Selenium settings section
was invisible on other PCs running the .jsx files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a new Settings → Advanced → Selenium Settings page where the office
name (paymentGroupId) used in United/DentalHub portal dropdowns can be
configured without touching code. Previously hardcoded as "Summit Dental
Care"; now injected from DB into the three United workers at job dispatch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AI chat extracts 'with provider <name>' and routes claim to that provider
- Claim form reads provider from sessionStorage before any async effects run,
preventing saved claim/procedure data from overriding the chatbot selection
- NPI provider settings table shows Provider #1 / #2 labels with up/down
reorder buttons; Provider #1 is always the default for claims
- Default provider now uses sortOrder instead of hardcoded 'Mary Scannell'
- Added sortOrder column to NpiProvider schema with migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add llm-factory.ts: unified LLM provider abstraction (Google/Claude/OpenAI)
- Install @langchain/anthropic and @langchain/openai packages
- resolveAiProvider picks active provider from DB settings (Claude > OpenAI > Google)
- All AI graphs (reminder, new-patient, reschedule, internal-chat) now accept provider+model params
- Add claudeAiModel, openAiModel, googleAiModel columns to ai_settings table
- New PUT /api/ai/provider-model route to save selected model per provider
- UI model dropdowns for Claude (Haiku/Sonnet/Opus), OpenAI (GPT-5.x series), Google (Gemini 2.5/3.x)
- Google AI section also gets model selector alongside existing API key field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add OpenAI, Claude AI, and DentalManagement AI sections to the AI API
Setting page, each with a masked API key input and an on/off toggle
(defaulting to off). Rename sidebar label from "Google AI Settings" to
"AI API Setting". Add provider-key and provider-enabled backend endpoints
and extend the AiSettings schema with 6 new fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add eligibility_by_id and check_and_claim intents to internal chat
- New cdt-lookup.ts: keyword search against fee schedule JSON (no LLM)
- New internal-chat-workflow.ts: deterministic orchestration — patient
resolution, insurance siteKey derivation, CDT code mapping
- Custom CDT aliases stored per-user in DB (TwilioSettings JSON blob)
with GET/PUT /api/ai/cdt-aliases endpoints
- Chatbot UI: new steps for eligibility-id-ready, check-and-claim-ready,
and need-insurance-clarification with insurance picker
- Settings UI: CDT Aliases CRUD table with built-in alias reference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New DialPad component on Patient Connection page: clickable keypad,
call/hangup/mute buttons, duration timer, keyboard input support
- Backend: POST /api/twilio/voice-token issues Access Token for browser
Device; POST /api/twilio/webhook/voice-browser is the TwiML webhook
Twilio calls to bridge the browser to the patient's phone
- TwiML App SID field added to Twilio Settings (stored in templates JSON)
- README: one-time Twilio Console setup instructions for the dial pad
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Gemini-powered internal staff chatbot (free-text input in the
upper-right bot panel): type "check MARIA GONZALES" to search patient
and pre-fill eligibility, or "open claims" to navigate directly
- Add /api/ai/internal-chat endpoint with LangGraph + Google Gemini
classifier (intent: check_eligibility, find_patient, navigate_*)
- Add Users AI Chat settings section in Settings > Advanced > AI Chat
to configure a custom system prompt for the internal assistant
- Store internal chat system prompt in existing twilioSettings JSON
blob (no DB migration needed)
- Add AI Input Agent sidebar entry and placeholder page describing
planned keyboard-automation typing into Open Dental / Eaglesoft
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New Selenium worker (fresh Chrome per run, no persistent session)
login → OTP modal → eTools → ConnectCenter → Verification →
New Eligibility Request → fill form (NPI, member ID, DOB) →
Expand All → CDP PDF back to app
- Backend route fetches BCBS_MA credentials + provider NPI from settings
- Frontend OTP modal with 6-digit code entry
- BCBS MA added to insurance credentials dropdown in settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add full Selenium automation for United/DentalHub claim submission
(steps 1-8: login, OTP, patient search, practitioner page, code entry,
other coverage No, attachments, submit, Status & History PDF)
- Consolidate UnitedDH siteKey to UNITED_SCO throughout app
- Fix procedure date overwrite with Ctrl+A+Delete before typing service date
- Fix OTP popup reliability: emit every poll (no throttle)
- Fix Chrome session persistence: only clear cookies on startup
- Add touchPatient() to storage: claim submission now pushes patient to
top of list across eligibility, claims, and documents pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the old after-hours/new-patient SVG with an accurate diagram of
the current conversation flow: 3-message TwiML entry → insurance check →
MassHealth consent + ID/DOB → Selenium eligibility → ACTIVE/INACTIVE paths,
inactive branch asks for other insurance then collects contact info, and
office-hours-validated date/time scheduling with appointment creation.
Existing patient branch shows same-insurance confirmation, MassHealth
auto-check (stored ID+DOB), and Selenium result paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 3-message intro (self-intro → empathetic ack → new/existing question) via single TwiML response to guarantee delivery order
- Detect reschedule intent from first message; look up existing appointment date
- New patient flow: ask insurance type → MassHealth consent → member ID + DOB → Selenium eligibility check
- Post-eligibility: active → ask appointment date/time with office-hours validation; inactive → ask other insurance or collect contact info
- Date/time collection mirrors reschedule flow: check office day open, ask time, validate against office hours
- Auto-create appointment in schedule for known patients on confirmation; use first available staff member
- Add openPhoneReply toggle (Settings → AI Chat) to respond to any number at any time
- Add 5-minute inactivity timeout: reset conversation to initial stage and clear pending state
- Normalize MassHealth DOB to zero-padded MM/DD/YYYY before Selenium submission
- Expand isExistingPatient classifier to recognize "old patient", "old", "previous", "prior"
- Existing patient confirmation message now acknowledges patient type before asking about insurance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reminder-graph: add stripIntroFromFallback() to remove 'Hi! My name is Lisa...'
from any saved rescheduleGreeting template before using it as the MSG 2 fallback
- reminder-graph: add explicit 'Do NOT introduce yourself' to rescheduleNode Gemini
prompt so the AI never adds its own intro to MSG 2
- ai-chat-templates-card: add hasIntroPattern() warning on the Reschedule Patients
template field — shows an amber alert if the saved template starts with a
self-introduction, guiding users to remove it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reminderSms default: remove {officeAddress} (never replaced by backend) to prevent
patients receiving literal '{officeAddress}' in reminder texts
- reminderGreeting default: fix typo 'reply you message' → 'reply to your message'
- rescheduleGreeting default: remove duplicate AI intro (intro is now sent separately
as MSG 1; fallback text should only contain the intent response)
- Add unsupportedVars() detector: highlights any {variable} in the SMS template that
the backend does not replace, with an amber warning showing the supported list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Reschedule for Column button on schedule page with AI follow-up toggle (default on)
- Add POST /api/twilio/send-reschedule-batch — sends Reschedule by Office template, starts AI reschedule conversation per patient
- Add {officePhone} (office call-in number) and {twilioPhone} (SMS number) variable replacement in both batch endpoints
- Fix broken variable names in Reschedule by Office template ({office phone number) → {officePhone}, {Twilio phone number} → {twilioPhone})
- Fix reschedule-graph: when patient replies with date in same message as YES/NO (e.g. "ok, 5/18"), AI now checks day open and asks for time instead of asking "what day and time?"
- Fix twilio-webhooks: same date-shortcut logic for reminder flow — "no, 5/18" skips "when to reschedule?" and goes straight to day check
- Update LangGraph SVG: rename to Reminder & Reschedule Flow, combine both entry points (Reminder SMS + Reschedule SMS) into one diagram with date-shortcut annotations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add streetAddress/city/state/zipCode fields to OfficeContact (schema + storage + UI)
- Support {officeAddress} variable in batch reminder SMS
- Replace single SMS template field with full CRUD template list (add/rename/edit/delete)
- Store SMS template list under _sms_template_list; first template synced to batch reminder
- Hardcode all AI chat template defaults into codebase (reminder SMS, greetings, fallback)
- Add seed-templates.ts that auto-seeds default templates for all users on server boot
- Update README: note that templates are auto-configured on first boot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the old ASAP/next-week/Mon-Tue-Wed/morning-afternoon subtree with
the new direct datetime flow:
- MSG 1: AI self-introduction (sent first via REST API)
- MSG 2: "When would you like to reschedule?" (intent response)
- Patient gives date → day-open check (office hours) with fail loop
- AI asks time → hours check (within AM/PM session) with fail loop
- AI confirms: "Just to confirm — [date at time]?"
- Patient YES/NO → slot availability check with fail loop
- DB: appointment moved, AI badge shown on schedule grid
- Patient thanks → closing message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Text Reminder for Column button with per-column checkboxes and AI follow-up toggle (default on)
- Batch reminder endpoint resolves {firstName}, {officeName}, {appointmentDate}, {appointmentTime} from AI chat templates
- Add Reschedule for Column UI (logic TBD)
- Move Download Claim PDF for Column below Reschedule for Column
- Add reminderSms template field to AI Chat Settings with variable hints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a member ID is typed on the insurance eligibility page, debounced
lookup fills in date of birth, first name, and last name if the patient
already exists in the database.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add floating chat window Hand-off to AI toggle (per-patient) and after-hours AI toggle (global)
- Add LangGraph-powered appointment reminder flow: AI introduces itself, classifies YES/NO, handles confirmation with appointment date/time
- Add multi-step rescheduling flow: ASAP vs next week, tomorrow offer, Mon/Tue/Wed picker, morning/afternoon time slot — automatically updates appointment in DB
- Add new patient / after-hours flow: new vs existing patient, dental insurance check, MassHealth Selenium eligibility check (auto-uses saved member ID + DOB for existing patients), self-pay fallback
- Add AI Chat Settings page (Settings → Advanced) with editable greeting templates and LangGraph flow diagrams for both reminder and new-patient flows
- Add Schedule a New Patient template option in chat window, starts new-patient conversation flow
- Add GET/PUT endpoints for AI handoff, after-hours handoff, and AI chat templates
- Add multilingual support (7 languages) across all AI reply nodes with LLM generation and hardcoded fallbacks
- Add pending reschedule in-memory store and conversation stage tracking across all flows
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Settings > Advanced > Procedure Duration/Time Slot page with three sections:
1. Procedure Duration: CDT codes with durations (editable table, save per section)
2. Doctor Time Slot: drag-to-block visual grid (A/B/C columns, 8 AM–9 PM, edit/delete slots)
3. Hygienist Time Slot: procedure descriptions with durations
- Backend: ProcedureTimeslot Prisma model, storage, and GET/PUT /api/procedure-timeslot route
- DB migration: procedure_timeslot table
- Appointment form: when type is "Other", show a free-text input for custom description; saved as other:<description> and decoded for display on the schedule card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Schedule columns default to labels A–F (localStorage, per-browser, click to rename)
- Settings → Advanced → Office Hours: configure Doctors (A-C) and Hygienists (D-F) AM/PM hours per weekday
- Gray out schedule slots outside office hours; override dialog for manual exceptions
- Override Office Hours toggle: select specific dates where all slots are open
- Fix appointment move: send only real DB fields to avoid Zod strict-mode rejection of computed fields (hasProcedures, hasClaimWithNumber)
- Fix backend PUT /appointments: safe error logging to prevent Prisma error crashing Node inspect
- Add OfficeHours Prisma model and GET/PUT /api/office-hours route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add /dentaquest-eligibility endpoint in Python agent (Tufts SCO uses providers.dentaquest.com)
- Add backend route, processor, and service client for Tufts SCO (separate from UnitedSCO/DentalHub)
- Fix Tufts SCO button to post to new tuftssco route instead of unitedsco
- Fix credential field names: dentaquestUsername/Password (was tuftsscoUsername/Password)
- Fix socket event: listen for selenium:dentaquest_session_started (was unitedsco)
- Fix error visibility: keep session alive 30s on error so backend reads real message
- Replace free-text Site Key field with dropdown to prevent key mismatches
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use useAuth() in CredentialTable instead of a separate /api/users/ fetch
that was failing silently and hiding the entire credentials section
- Add migration for autoBackupEnabled/usbBackupEnabled columns on User
that were in the Prisma schema but missing from the database, causing
a 500 error on login
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>