Fresh-machine setup guide didn't cover the Payment OCR Service's Google
Vision credentials — add it as its own section (enable API, create
service-account key, install as google_credentials.json, verify).
- README: step-by-step Google Cloud Vision API key creation and setup
(enable API, create service-account key, place as google_credentials.json)
- ocrLocate: split into getOcrWords()/matchWordsForText() so a screenshot is
OCR'd once and matched locally against multiple text targets, instead of a
separate billed Vision API call per target (patient-row and Exam steps each
cut from 2-3 calls down to 1)
Claude vision's self-reported text coordinates had a small but consistent bias,
causing the patient-row double-click to land above the name and the Exam-column
divider count to occasionally miscount and hard-fail. Swap patient-row, Exam,
and Save button targeting to exact OCR bounding boxes (Google Vision via the
existing PaymentOCRService) instead of AI-estimated ratios.
schema.prisma had autoMhCheckEnabled/DayOfWeek/Hour on User (added in
fc3e8c0e) with no corresponding migration, so migrate deploy on a fresh
DB left those columns missing and broke seeding. Also drops
lab_rx_template.doctorName, a column that existed only in the DB (added
by an old migration) but was never in schema.prisma or referenced by
any app code.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Clicking "Add a file" triggers input.click() which opens the OS-native
file picker that WebDriver can't control, causing send_keys on the
underlying input to fail with a message-less exception. The input is
already present in the DOM (display:none) so send_keys can target it
directly without the click.
_fill_combobox typed the value then used a single unioned XPath
(match-by-text | option[1]) to pick the dropdown option. Selenium's
find_element on a multi-match XPath returns the first node in
document order, not the first union alternative, so option[1] (e.g.
tooth "1") was always clicked regardless of what was typed (e.g. "22").
Split into a value-match wait with a separate fallback wait for
option[1] only when no match is found.
Deleting/replacing an appointment attachment left an orphaned ClaimFile
copy behind (auto-linked earlier by autoLinkAppointmentFiles), causing
the Selenium claim automation to upload extra files beyond what's
currently attached to the appointment. Claim PUT was also append-only
for claimFiles, so removing an attachment in the UI never persisted.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Row selection previously computed the target row's y arithmetically
(header.y + rowHeight * rowIndex), which drifted further off-row the
deeper the target patient was in the filtered list, and an AI
"onTarget" verification step added to catch drift instead confirmed a
click point that was visibly in blank space below the grid.
locateAllOnScreenshot now reports each text match's top/bottom edges
instead of a self-estimated center, and the patient row step locates
the target's first and last name directly, pairing whichever
occurrences land on the same row instead of trusting arithmetic or a
qualitative AI judgment call.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces fragile whole-screen text matching with a screenshot-diff step that
finds each dialog's actual pixel bounds, then restricts every subsequent AI
locate/click to that cropped region — eliminating false matches from text
elsewhere on screen (title bars, side panels). Adds column-boundary and
row-height detection so the patient row and Exam procedure click positions
are computed geometrically instead of relying on repeated fuzzy AI guesses
for visually similar neighbors. Also adds per-run screenshot/debug-crop
backups and a structured run.log for diagnosing failed runs, plus a
cmd:move primitive on the Windows agent for pre-click confirmation crops.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a Windows-side agent (screenshot/click/type over HTTP) plus backend
services to locate UI elements via vision and drive an existing-patient
appointment flow in Open Dental, wired into the Copy/Type Agent page and
socket progress updates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ccaPreAuthProcessor: keep claim status as PREAUTH after successful
submission instead of jumping straight to APPROVED, matching the
Tufts SCO/UnitedDH/MassHealth preauth flows; also tag PNG fallback
screenshots with image/png instead of the default application/pdf
so they render in the Documents preview modal.
- claims-recent-table: add a PREAUTH status case so it displays as
"Submitted" instead of falling through to the raw enum value.
- Fix NameError (col_idx vs col_expr) that silently corrupted grid-fill logs
- Poll for new grid rows to render before clicking, instead of failing immediately
- Abort submission with a clear error if code/tooth/billed amount fail to fill,
instead of silently submitting an incomplete claim
- Compute grid column positions live from header metadata instead of stale
hardcoded indices; billed amount was landing on a hidden helper column
(TAB_FROM_CODE_CELL) via td[last()] instead of the real Billed Amt column
- Read claim/encounter number from the top (newest) dashboard row instead
of the bottom row
skipAppointmentAttachmentMerge was blanket-set for any autoSubmit path
(chatbot, Claim with AI batch automation, insurance-status auto-check),
so screenshots/files attached directly on the appointment were silently
dropped from the MassHealth Selenium upload whenever a claim was
auto-submitted. Now the merge is only skipped if the automated flow
actually supplied its own files.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Chatbot: submitting with only a screenshot attached (no text) now triggers the same
"AI Detect & Check Eligibility" flow as the Copy Agent page
- detect-eligibility-info now also extracts the visible insurance payer name and picks
Tufts SCO auto-check when both Sun Life and DentaQuest are detected, else MassHealth
- Chatbot-attached files are now handed off to both claim and preauth forms unconditionally,
not just claims
- TuftsSCO preauth Selenium worker: verify-and-retry the Tooth field (typing was silently
getting reset by duplicate-procedure-code warning banners), add a final re-verification
pass across all rows, fix acknowledgement-checkbox targeting/verification, and verify
the "Next step" click actually advances the wizard instead of trusting a blind click
- selenium-claim route no longer re-appends previously saved appointment
attachments when the chatbot's autoSubmit flow already sent every intended
file, preventing an unwanted extra upload (e.g. attach 2 -> Selenium sees 3).
- Add a "Take screenshot" button to the AI chatbot's attachment bar, reusing
the same capture/crop flow as the appointment editor, so claims and
preauths can be submitted from the chatbot with a live screenshot attached.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add "surg ext" alias mapping to D7210 in the AI CDT lookup.
- Add GET/DELETE endpoints for appointment attachments, and load existing
attachments in the appointment edit modal (they were uploaded correctly
but never re-fetched, so they appeared to vanish on reopen).
- Fix claim-form.tsx so previously-saved appointment attachments are always
loaded (even when a claim already exists) instead of only when creating a
brand-new claim, preventing them from being silently wiped on save.
- Merge existing appointment attachments into every insurance submit handler
(MassHealth, CCA, DDMA, UnitedDH, TuftsSCO) so Selenium submission always
includes files saved earlier via the Schedule editor, not just files
uploaded in the current claim-form session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an optional Provider (NPI) select to the Edit Appointment form,
sourced from Settings > NPI Providers. The AI "Claim for Column" batch
flow now prefers the appointment's own provider when resolving which
provider Selenium should select on the insurance site (e.g. MassHealth
rendering provider dropdown), falling back to the existing per-procedure
selection, active claim, or first configured provider.
Also resyncs packages/db's committed compiled schema/client artifacts
with their .ts sources — they had drifted out of sync (some untouched
since July), which was silently stripping the new npiProviderId field
via stale .strict() zod schemas.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Member ID and DOB in the AI chat "patient found" confirmation card are now
editable inputs, and edits are persisted back to the patient record before
proceeding so corrections don't revert to stale values.
- Fix auto-lookup-by-member-ID effect on the insurance status page clobbering
a hand-edited DOB with the stale DB value during chatbot prefill.
- CMSP (under-14) eligibility flow was missing the appointment-creation hook
that the adult MassHealth flow already had, so under-14 patients never got
scheduled after a chatbot-initiated "eligibility & appointment" request.
- For already-known patients, the chatbot was creating the appointment before
running the eligibility check instead of after; now eligibility is always
checked first and the appointment created only afterward, and only when the
resulting patient status is ACTIVE.
MassHealth eligibility lookups via the AI chatbot only matched existing
patients on memberId+DOB, so patients imported from PDF extraction (which
have a member ID but no stored birthday) were treated as new/unknown even
though they already exist in the system.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The core service already auto-spawns its own tray icon per session, so
Step 18's autostart entry is specifically for opening the main window.
Also note that a permanent password avoids the OTP-refresh confusion
where the window briefly shows one password then updates to another.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Autologin skips password entry, so the login keyring never unlocks and
apps keep popping "Authentication required". Document removing it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets staff attach files to an appointment directly from the edit form via
local disk upload, picking a previously-saved scanned attachment, or an
inline screenshot with crop-before-save (matching the Copy Agent page's
crop UX), instead of detouring through the Copy Agent page. Also fixes a
latent data-loss bug in claim-form's save-for-appointment call, which
replaced all AppointmentFile rows instead of merging with existing ones.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- AI claim queue now uses previously saved AppointmentProcedure data (with a derived insurance site key) when an appointment has no notes but has saved procedures, instead of skipping with "No notes on this appointment"
- selenium-claim route now reads saved AppointmentFile attachments from disk when they weren't re-uploaded in the browser, so images/PDFs already attached via "Select Procedures" reach the MassHealth submission
Adds From/To date pickers and CCA PA / United PA / Tufts PA / All recent PA
buttons above the Upload claim documents section; button logic is stubbed
for now.
Selecting a patient from search results only stored the ID, not the
patient object. Closing the dropdown reset the search term and reverted
the list to "recent 50" patients, which often didn't include the just
selected patient, leaving nothing to render in the trigger. Also removed
a redundant prefillPatient reset that nulled the fallback display before
the list swap even happened.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets a user override the default MassHealth check on the chatbot's
eligibility-id-ready confirm card (e.g. switch to United SCO, CMSP,
DDMA, Delta Dental, Tufts SCO, or CCA) before running the check.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an "AI Detect & Check Eligibility" action to the Copy Agent page that
sends captured screenshots to Claude to extract Member ID/DOB, then opens
the chatbot's eligibility confirm step pre-filled with the result.
- POST /api/ai/detect-eligibility-info: Claude vision extraction of
Member ID/DOB from uploaded screenshots.
- POST /api/patients/create-temp: auto-creates a placeholder "PID-<id>"
patient (matched by insuranceId+dob) when no patient is selected, so
screenshots can be saved immediately; the real eligibility check later
rewrites the patient's name and Cloud Storage folder automatically via
the existing createOrUpdatePatientByInsuranceId matching logic.
- Chatbot: Member ID/DOB on the confirm card are now editable (MM/DD/YYYY),
with edits synced back to the patient record before the check runs.
- Fixed a useEffect ordering bug on the insurance-status page that could
silently clear a chatbot-prefilled Member ID/DOB before the auto-trigger
effect ran, preventing the eligibility check from ever starting.
- Added a local-disk-only screenshot backup (apps/Backend/uploads/
screenshot-backups/) for debugging, not exposed anywhere in the UI.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Native file inputs can't be pointed at a default server folder for security
reasons, so instead the Browse buttons on the Claim/PreAuth/Select Procedures
forms and the AI chatbot's attach button now also offer picking from
previously saved Cloud Storage attachments (e.g. Copy Agent screenshots) via
a new server-side picker modal, scoped per-patient on the claim forms and
unscoped (recent across all patients) in the chatbot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Some routers (confirmed on Verizon Fios/G3100) block the local-<office>
hostname from resolving because it's a public domain pointing at a
private LAN IP, which matches the DNS rebinding attack pattern. Document
the router-level fix (DNS Rebind Protection exception) and the per-machine
hosts-file workaround for both the Debian server and Windows staff PCs,
including the antivirus hosts-file-lock gotcha on Windows.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the actual config used for Summit Dental Care, where the tunnel
points at nginx's port-80 Twilio-only block instead of the whole app,
so only /api/twilio/* is reachable from the internet.
Documents the trusted-certificate setup used for local staff access,
needed because the Screen Capture API (Copy Agent screenshots) requires
a secure context.
getDisplayMedia (screen capture) requires a secure context, so LAN
staff access now goes through nginx on a real trusted cert
(local-summit.mydentalofficemanagement.com, issued via certbot's
Cloudflare DNS-01 plugin) instead of plain HTTP. A separate public
hostname/nginx block is reserved for Twilio webhooks only, routed
through Cloudflare Tunnel so nothing else is exposed to the internet.
Also fixes the backend CORS allowlist for the new hostname, and stops
the Socket.IO client from bypassing nginx to hit the backend directly
on :5000 (which broke under TLS/mixed content).
Staff can now capture their screen via the browser's Screen Capture
API, crop the result, name the file, and save it directly to a
selected patient's Cloud Storage attachments folder.
filesForQueue was collected from AppointmentFile/ClaimFile records but only
wired into the MH and CCA job payloads. The other three insurers' Selenium
workers read claim.claimFiles and always got an empty list, so attachments
already saved to Cloud Storage silently never got attached to the claim.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Missed these five Number(...) coercions when widening PDF ids to support the
"cloud:<id>" format — member details/history/accumulator panels in the MH and
CMSP multi-PDF viewers were fed NaN, which usePdfBlob treats as falsy and
never fetches. Only the first panel (eligibility) used the already-fixed
pdfFileId field, so it worked while the rest silently stayed blank.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- PDF viewer hung on "Loading" forever for cloud-backed PDFs: streamFileTo
pipes with {end: false} (by design, so cloud-storage.ts's own routes can
call res.end() themselves), but the new /pdf-files/:id cloud branch never
did, so the HTTP response never completed.
- Documents page showed nothing for patients whose PDFs are only in Cloud
Storage: the page lists categories from the legacy PdfGroup table, which
new saves stopped populating. savePdfToCloudStorage now also ensures a
blob-less PdfGroup row exists per patient/category so the category still
appears, with actual files served from Cloud Storage via the existing
merge in the pdf-files/group listing routes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Site now uses a dedicated claims-submission page with a Subscriber ID +
Birthdate search form instead of the member-ID search box, and login
detection now checks for the check-patient-benefits-search link rather
than the old member-search input placeholder.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New saves go into the patient's Cloud Storage folder under category subfolders
(Eligibility/Claims/PreAuth/Claim Status/Attachments) via a new
storage.savePdfToCloudStorage helper, instead of the legacy PdfGroup/PdfFile
Postgres blob tables. Old records and their read paths are left untouched for
backward compatibility; PDF list/content/delete endpoints and viewers now
transparently handle both legacy numeric ids and new "cloud:<id>" ids.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Renames the sidebar entry to "AI Copy/Type Agent" with Copy Agent, Type
Agent, and Settings sub-pages. Settings shows the server's LAN IP
address (via a new /api/ai/network-info endpoint) since it changes
depending on which office the app is running in.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a second seeded account (User1/123456) so a non-admin login exists by default for testing role-based UI restrictions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Delta Dental MA moved their provider login from providers.deltadentalma.com/onboarding
to www.deltadentalma.com/sign-in with new form field selectors, and moved eligibility
search behind a "Group/Health Connector" search page keyed by subscriber ID + birthdate
instead of the old member-search form. Updates login, OTP/MFA handling, and member
search/selection across the eligibility and claim DDMA workers and their session helpers.