lua-cli 3.33.0; run it with the steps on Running any example. The example shows the integration shape; it is not a compliance review.
Verified against lua-cli 3.33.0.
The conversation
- The patient’s first message is blocked by
hipaa-consentwith a request to reply “I consent”. When they do, the preprocessor records the consent on their profile and lets the message through; later messages pass without a check. - The patient asks for an appointment.
schedule_appointmentreadspatientIdfrom the profile and posts a FHIRAppointmentfor that patient and the chosen practitioner. - The patient asks about their medications.
view_medical_recordsreads the matching FHIR resources for the same patient id. medical-disclaimerappends one fixed sentence to every reply.
Primitives and channels
- Skill and tools:
patient-portal, withschedule_appointmentandview_medical_records. - Preprocessor
hipaa-consentat priority 10, and postprocessormedical-disclaimer. - Runtime objects:
User.get,user.update, andenvforEMR_API_URLandEMR_API_KEY. - Channels: the web widget inside the signed-in portal, where the portal can write
patientIdto the profile. Any other channel works once that field is set.
The code
The tools refuse when the profile has nopatientId; the skill’s context tells the model what to do then.
src/skills/tools/ScheduleAppointmentTool.ts
src/skills/tools/ViewMedicalRecordsTool.ts
src/skills/patient-portal.skill.ts
block ends the turn with its response, and proceed lets the message through.
src/preprocessors/hipaaConsent.ts
src/postprocessors/medicalDisclaimer.ts
src/index.ts
First run
Run the preprocessor with a first message on the web widget’s channel,pop; it blocks and returns the consent request.
Output
"message":"I consent" proceeds and writes hipaaConsentGiven to the test user’s profile. Test a tool with lua test --ci skill --name view_medical_records --input '{"recordType":"Condition"}'; without patientId on the profile it returns the not-verified reason. For the full loop with the model, send lua chat --ci -e sandbox -m "Can I see my medications?" -t; it uploads the .env values with the sandbox version. Then release it with lua push all --ci --force, lua version create --ci -m "<message>", and lua version promote <n>; Release an agent to production explains what each command changes.
Ways to make it yours
- Write
patientIdto the profile after your portal verifies identity, either from a verification tool withuser.update({ patientId })or through the user data REST endpoints. The tools trust that field and nothing the model says. - Replace the consent text and the disclaimer with the wording your compliance team approves; both are strings in one file each.
- Vary the disclaimer by channel with the fourth argument of the postprocessor’s
execute(popfor the web widget,whatsappfor WhatsApp), for example a shorter sentence on WhatsApp. - Summarize FHIR resources into plain fields inside
view_medical_recordswhen the model’s replies are too raw; the tool’s return value is what the model reads.
Next steps
Add a processor
Order, priority, blocking, and testing preprocessors and postprocessors.
Identify users
The profile, custom fields, and cross-channel identity.
Call your API
Keys in
env(), errors, and timeouts when a tool calls an external service.
