Commit Graph

13 Commits

Author SHA1 Message Date
a00668c517 feat(ai): UUID-safe agent tools + lookup_lead_activities + tool logging
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Bug 553 (partial) — AI Panel 'Patient History' returned 'not in system'
even though the caller had 7 calls + an appointment. The model was
hallucinating instead of chaining lookup tools.

UUID safety: LLMs drop hyphens / swap chars on 36-char ids once the
context wears thin. To keep the model off the UUID path for 'this
caller' questions:
 - lookup_appointments, lookup_call_history, lookup_lead_activities
   now accept their id arguments OPTIONALLY
 - when omitted, the sidecar resolves leadId from ctx and patientId
   from the lead record (cached per-request)
 - new lookup_lead_activities tool rounds out the patient-history
   trio (call history + activity log + appointments)

System prompt (ccAgentHelper) tightened:
 - chain call history + activities + appointments for history questions
 - call lookup tools with NO arguments when using the current caller
 - don't re-type UUIDs seen in CURRENT CONTEXT
 - say 'feature not set up yet' when KB section is empty (packages,
   etc.) instead of 'I couldn't find that'

All agent tools now emit structured [AI-TOOL] trace lines with full
UUIDs printed — tail sidecar logs to see which tool the model chose,
whether the model passed an id or used the context fallback, and how
many records came back. If the model ever hallucinates a UUID, the
resolved= field on the log line will echo it and count=0 will flag
the miss immediately.
2026-04-16 05:39:08 +05:30
6adb3985cb feat(config): ui-flags endpoint driven by HELIX_SETUP_MANAGED
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Per-tenant flag that hides self-serve setup surfaces when the product team owns onboarding for a workspace. Set HELIX_SETUP_MANAGED=true on the sidecar env for that tenant; the frontend reads this endpoint at boot, hides the Settings nav + Setup banner, and blocks /settings/* routes. Setup-state APIs stay live so ops can still drive the wizard remotely.
2026-04-15 18:55:25 +05:30
fbe782b5ac fix+feat: morning QA fixes, worklist pagination, misc sidecar improvements
- caller-resolution: drop cache, use indexed phone filter (lead.contactPhone.primaryPhoneNumber.like)
- worklist: externalize page size (WORKLIST_PAGE_SIZE × WORKLIST_MAX_PAGES), paginate getMissedCalls/getAssignedLeads/getPendingFollowUps
- maint: unlock-agent, force-ready, backfill-caller-resolution, clear-analysis-cache, fix-timestamps
- ozonetel agent.service: force logout+re-login on "already logged in"
- ai chat: context expansion
- livekit-agent: updates
- widget: session handling
- masterdata: clinic list cache

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:49:02 +05:30
27a3fbcfed feat(config): add Ozonetel admin credentials to TelephonyConfig
- adminUsername + adminPassword in ozonetel section
- Masked in GET response, sentinel-stripped on update
- Env seeds: OZONETEL_ADMIN_USERNAME, OZONETEL_ADMIN_PASSWORD
- Used by supervisor barge/whisper/listen endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 16:03:51 +05:30
4bd08a9b02 fix: remove defaultAgentId fallback — require agentId from caller
agent-state, dispose, dial, performance, force-ready, unlock-agent
all required agentId from the request body now. No silent fallback
to OZONETEL_AGENT_ID env var which caused cross-tenant operations
in multi-agent setups (Ramaiah operations hitting Global's agent).

OZONETEL_AGENT_ID removed from telephony env seed list. Hardcoded
fallbacks (agent3, Test123$, 521814) deleted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:10:31 +05:30
695f119c2b feat: team module, multi-stage Dockerfile, doctor utils, AI config overhaul
- Team module: POST /api/team/members (in-place employee creation with
  temp password + Redis cache), PUT /api/team/members/:id, GET temp
  password endpoint. Uses signUpInWorkspace — no email invites.
- Dockerfile: rewritten as multi-stage build (builder + runtime) so
  native modules compile for target arch. Fixes darwin→linux crash.
- .dockerignore: exclude dist, node_modules, .env, .git, data/
- package-lock.json: regenerated against public npmjs.org (was
  pointing at localhost:4873 Verdaccio — broke docker builds)
- Doctor utils: shared DOCTOR_VISIT_SLOTS_FRAGMENT + normalizeDoctors
  helper for visit-slot-aware queries across 6 consumers
- AI config: full admin CRUD (GET/PUT/POST reset), workspace-scoped
  setup-state with workspace ID isolation, AI prompt defaults overhaul
- Agent config: camelCase field fix for SDK-synced workspaces
- Session service: workspace-scoped Redis key prefixing for setup state
- Recordings/supervisor/widget services: updated to use doctor-utils
  shared fragments instead of inline visitingHours queries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:37:58 +05:30
619e9ab405 feat(onboarding/phase-1): admin-editable telephony, ai, and setup-state config
Phase 1 of hospital onboarding & self-service plan
(docs/superpowers/plans/2026-04-06-hospital-onboarding-self-service.md).

Backend foundations to support the upcoming staff-portal Settings hub and
6-step setup wizard. No frontend in this phase.

New config services (mirroring ThemeService / WidgetConfigService):
- SetupStateService    — tracks completion of 6 wizard steps; isWizardRequired()
                         drives the post-login redirect
- TelephonyConfigService — Ozonetel + Exotel + SIP, replaces 8 env vars,
                           seeds from env on first boot, masks secrets on GET,
                           '***masked***' sentinel on PUT means "keep existing"
- AiConfigService      — provider, model, temperature, system prompt addendum;
                         API keys remain in env

New endpoints under /api/config:
- GET  /api/config/setup-state                returns state + wizardRequired flag
- PUT  /api/config/setup-state/steps/:step    mark step complete/incomplete
- POST /api/config/setup-state/dismiss        dismiss wizard
- POST /api/config/setup-state/reset
- GET  /api/config/telephony                  masked
- PUT  /api/config/telephony
- POST /api/config/telephony/reset
- GET  /api/config/ai
- PUT  /api/config/ai
- POST /api/config/ai/reset

ConfigThemeModule is now @Global() so the new sidecar config services are
injectable from AuthModule, OzonetelAgentModule, MaintModule without creating
a circular dependency (ConfigThemeModule already imports AuthModule for
SessionService).

Migrated 11 env-var read sites to use the new services:
- ozonetel-agent.service: exotel API + ozonetel did/sipId via read-through getters
- ozonetel-agent.controller: defaultAgentId/Password/SipId via getters
- kookoo-ivr.controller: sipId/callerId via getters
- auth.controller: OZONETEL_AGENT_PASSWORD (login + logout)
- agent-config.service: sipDomain/wsPort/campaignName via getters
- maint.controller: forceReady + unlockAgent
- ai-provider: createAiModel and isAiConfigured refactored to pure factories
  taking AiProviderOpts; no more ConfigService dependency
- widget-chat.service, recordings.service, ai-enrichment.service,
  ai-chat.controller, ai-insight.consumer, call-assist.service: each builds
  the AI model from AiConfigService.getConfig() + ConfigService API keys

Hot-reload guarantee: every consumer reads via a getter or builds per-call,
so admin updates take effect without sidecar restart. WidgetChatService
specifically rebuilds the model on each streamReply().

Bug fix bundled: dropped widget.json.hospitalName field (the original
duplicate that started this whole thread). WidgetConfigService now reads
brand.hospitalName from ThemeService at the 2 generateKey call sites.
Single source of truth for hospital name is workspace branding.

First-boot env seeding: TelephonyConfigService and AiConfigService both
copy their respective env vars into a fresh data/*.json on onModuleInit if
the file doesn't exist. Existing deployments auto-migrate without manual
intervention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 07:02:07 +05:30
e6c8d950ea feat: widget config via admin-editable data/widget.json
Mirrors the existing theme config pattern so website widget settings can be
edited from the admin portal instead of baked into frontend env vars. Fixes
the current symptom where the staging widget is silently disabled because
VITE_WIDGET_KEY is missing from .env.production.

Backend (sidecar):
- src/config/widget.defaults.ts — WidgetConfig type + defaults
  (enabled, key, siteId, url, allowedOrigins, hospitalName,
  embed.loginPage, version, updatedAt)
- src/config/widget-config.service.ts — file-backed load / update /
  rotate-key / reset with backups, mirroring ThemeService. On module init:
    * first boot → auto-generates an HMAC-signed site key via
      WidgetKeysService, persists both to data/widget.json and to Redis
    * subsequent boots → re-registers the key in Redis if missing (handles
      Redis flushes so validateKey() keeps working without admin action)
- src/config/widget-config.controller.ts — new endpoints under /api/config:
    GET  /api/config/widget            public subset {enabled, key, url, embed}
    GET  /api/config/widget/admin      full config for the settings UI
    PUT  /api/config/widget            admin update (partial merge)
    POST /api/config/widget/rotate-key revoke old siteId + mint a new key
    POST /api/config/widget/reset      reset to defaults + regenerate
- Move src/widget/widget-keys.service.ts → src/config/widget-keys.service.ts
  (it's a config-layer concern now, not widget-layer). config-theme.module
  becomes the owner, imports AuthModule for SessionService, and exports
  WidgetKeysService + WidgetConfigService alongside ThemeService.
- widget.module stops providing WidgetKeysService (it imports ConfigThemeModule
  already, so the guard + controller still get it via DI).
- .gitignore data/widget.json + data/widget-backups/ so each environment
  auto-generates its own instance-specific key instead of sharing one via git.

TODO (flagged, out of scope for this pass):
- Protect admin endpoints with an auth guard when settings UI ships.
- Set WIDGET_SECRET env var in staging (currently falls back to the
  hardcoded default in widget-keys.service.ts).
- Admin portal settings page for editing widget config (mirror branding-settings).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:33:25 +05:30
8cc1bdc812 feat: theme config service — REST API with versioning + backup
- ThemeService: read/write/validate theme.json, auto-backup on save
- ThemeController: GET/PUT/POST /api/config/theme (public GET, versioned PUT)
- ThemeConfig type with version + updatedAt fields
- Default theme: Global Hospital blue scale
- ConfigThemeModule registered in AppModule

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:50:51 +05:30
e4a24feedb feat: multi-agent SIP with Redis session lockout
- SessionService: Redis-backed session lock/unlock/refresh with 1hr TTL
- AgentConfigService: queries Agent entity, caches per-member config
- Auth login: resolves agent config, locks Redis session, returns SIP credentials
- Auth logout: unlocks Redis session, Ozonetel logout, clears cache
- Auth heartbeat: refreshes Redis TTL every 5 minutes
- Added ioredis dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:24:32 +05:30
cec2526d37 feat: Phase 2 — missed call queue ingestion, auto-assignment, endpoints
- MissedQueueService: polls Ozonetel abandonCalls every 30s, dedup by phone
- Auto-assigns oldest PENDING_CALLBACK call on agent Ready (dispose + state change)
- GET /api/worklist/missed-queue, PATCH /api/worklist/missed-queue/:id/status
- Worklist query updated with callback fields and FIFO ordering
- PlatformGraphqlService.query() made public for server-to-server ops
- forwardRef circular dependency resolution between WorklistModule and OzonetelAgentModule

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:17:33 +05:30
9688d5144e feat: migrate AI to Vercel AI SDK, add OpenAI provider, fix worklist
- Replace raw @anthropic-ai/sdk with Vercel AI SDK (generateText, tool, generateObject)
- Add provider abstraction (ai-provider.ts) — swap OpenAI/Anthropic via env var
- AI chat controller: dynamic KB from platform (clinics, packages, insurance), zero hardcoding
- AI enrichment service: use generateObject with Zod schema instead of manual JSON parsing
- Worklist: resolve agent name from platform currentUser API instead of JWT decode
- Worklist: fix GraphQL field names to match platform remapping (source, status, direction, etc.)
- Config: add AI_PROVIDER, AI_MODEL, OPENAI_API_KEY env vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:45:05 +05:30
a3172140b0 chore: scaffold NestJS sidecar with config, CORS, and dependency setup 2026-03-17 09:02:15 +05:30