# Exult RingCentral Backup Scheduling Voice Agent

This package contains the overflow/rescheduling voice-agent runtime for Exult.
RingCentral stays the telephony front door, AdvancedMD stays the scheduling
source of truth, and the realtime voice provider is swappable.

## Current Runtime Shape

- Telephony media: RingCentral Cloud Phone SDK via `ringcentral-softphone`.
- Primary voice provider: OpenAI Realtime.
- Alternate voice provider: xAI/Grok Voice through the same provider adapter,
  blocked for PHI unless `XAI_PHI_ALLOWED=1`.
- Optional connect-time failover: set `VOICE_PROVIDER_FAILOVER=1` with
  OpenAI as primary and xAI credentials approved for PHI.
- Scheduling tools: server-side policy proxy. The model receives only scoped
  tool definitions and never receives AMD or RingCentral credentials.
- AMD write safety: `request_reschedule` is blocked unless
  `verify_patient_identity` has positively identified the same patient in the
  same call session. The live port still fails closed until a narrow appointment
  write gateway is connected; do not enable unrestricted AMD `raw_amd_call` for
  the voice agent.
- Audit: live calls append private JSONL records to
  `VOICE_AGENT_AUDIT_LOG` or `~/.local/state/exult-voice-agent/audit.jsonl`.
  The store creates 0700 directories and 0600 files.
- Call start: the runtime prompts the voice provider to say exactly,
  "Hi, this is Exult Healthcare. How can I help you?" The recording disclosure
  happens before collecting identifiers for rescheduling.

## Useful Commands

```bash
bun run mcp:smoke
bun run provision:plan
bun run sip:discover
bun run preflight:live
bun run start:live
bun run audit:reconcile
bun test
bun run typecheck
```

`preflight:live` must pass before the systemd service will start. The service
wrapper intentionally refuses to run the live agent if the provider credential,
hosted MCP transport, or RingCentral SIP device checks fail. A separate
Realtime smoke is still required before a live call, because OpenAI/xAI can
accept a key for read endpoints while rejecting voice sessions for quota or
billing reasons.

## RingCentral Approval Boundary

Do not run RingCentral writes without Gautam's fresh approval in the current
conversation. Use `bun run provision:plan` to print the exact approval text.

The original automated write scope was intentionally narrow:

- Completed after Gautam's approval: create enabled extension `9001` named
  `AI Scheduling Backup`, provision one `OtherPhone` SIP device, and assign a
  direct number recorded in private ops notes.
- Not automated yet: queue 55 routing, IVR 2000 routing, greetings,
  forwarding, or caller ID changes.

`create_ai_scheduling_extension` is guarded on the hosted
`ringcentral-admin` MCP by both exact approval text and
`EXULT_RC_ALLOW_WRITES=1`.

## Other Phone Device

The live media bridge needs a dedicated RingCentral `Other Phone` /
`Existing Phone` device so `sip:discover` can read SIP registration details.

RingCentral's public API docs show:

- User creation uses `POST /restapi/v1.0/account/~/extension` and requires
  privileged `EditAccounts` scope.
- BYOD device inventory uses
  `POST /restapi/v2/accounts/{accountId}/device-inventory`, marked limited in
  the API reference.
- Batch user/device provisioning uses
  `POST /restapi/v2/accounts/{accountId}/batch-provisioning/users` and its
  `DeviceDefinition` includes emergency-address and phone-number selection
  fields.

Because BYOD device provisioning may affect billing, E911, digital lines, and
phone-number assignment, the current implementation leaves the `Other Phone`
device as an Admin Console step until Gautam separately approves the exact API
payload and account-impact policy.

`POST /restapi/v1.0/client-info/sip-provision` is intentionally not used as a
shortcut. RingCentral documents it as SIP registration for a device/application
under the authenticated calling context; it does not establish the dedicated
AI extension/device ownership or routing boundary that the production bridge
needs for reporting and approval control.

References:

- https://developers.ringcentral.com/guide/account/creating-users
- https://developers.ringcentral.com/api-reference

## Live Bring-Up Order

1. Run `bun run provision:plan` and get Gautam's exact RingCentral approval.
2. During the approved write window, create extension `9001` only.
3. Add the `AI Scheduling Backup SIP` Existing Phone / Other Phone device.
4. Run `bun run sip:discover` to write or copy SIP env values.
5. Set `OPENAI_API_KEY` and `VOICE_AGENT_AUDIT_LOG` in
   `~/.config/voice-agent.env`.
6. Run `bun run preflight:live`.
7. Start the disabled systemd user service only after preflight passes.
8. Place a direct test call to the AI extension before requesting any queue,
   after-hours, IVR, or manual-failover routing approval.

## Audit Reconciliation

`bun run audit:reconcile` reads the local audit JSONL, pulls detailed
RingCentral Admin MCP call logs, and writes a reconciled JSONL file. Matching
uses `telephonySessionId`, then `sessionId`, then bridge `callId`, and extracts
call-log IDs plus recording `contentUri` when present.
