import type { AvailableSession, CreateSessionResult, InviteTeamResult, JoinSessionResult, RemoveParticipantResult, SessionStatusResult, ShareItemResult, VideoContext, VideoSessionRecord, } from './videoContract'; /** `admit` runs again immediately before the request is sent, after every token wait, and throws to keep it unsent. */ export interface LeaveOptions { admit?: () => void; } /** CONTROL PLANE = OPTION A (Spencer, 2026-08-01): HTTP in, WebSocket out. Every method here is an * authenticated HTTP call on the broker; the routes and response shapes are transcribed from the * server design doc §3.1. There is deliberately no `send()` - the socket is inbound-only, so a new * client server action becomes a method on THIS interface, never a WS message. A refusal arrives as * an HTTP status, not as a boolean that conflated "denied" with "your socket is down". */ export interface VideoTransport { /** `POST /R4/video-session` - practitioner-only server-side; the host is an attendee immediately. */ createSession(context: VideoContext): Promise; /** `POST /R4/video-session/{id}/join` */ joinSession(sessionId: string): Promise; /** `POST /R4/video-session/{id}/leave` - "mark me gone." ANYONE may leave, and this is what every * participant-departing path uses. The returned `status` is the point: `'active'` means others are * still in the room and the meeting lives on; `'ended'` means you were the last one out and the * SERVER did the `DeleteMeeting` for you. Leaving is never the caller's decision to end. */ leaveSession(sessionId: string, options?: LeaveOptions): Promise; /** `POST /R4/video-session/{id}/end` - HOST ONLY (server design §3.1: `allowedRoles` is * `PRACTITIONERS`, and the handler additionally checks the caller IS the host). Always does a Chime * `DeleteMeeting`: the meeting ends for EVERYONE and that is **not recoverable** - the practitioner * would have to start a fresh session and every participant rejoin. * * **NOTHING THAT REPRESENTS "A PARTICIPANT IS LEAVING" MAY CALL THIS.** That conflation is the * defect Lane A caught in the shipped client (§3.3): `endCall` routed here, so a patient clicking * "Leave" tore the meeting down for their own doctor. Use `leaveSession`. This verb exists for a * practitioner deliberately finishing the visit, and a non-host calling it is refused (403). */ endSession(sessionId: string): Promise; /** * `POST /R4/video-session/{id}/remove` - host only. Ejects one other person; the meeting keeps * running. Neither `leaveSession` (marks the caller gone) nor `endSession` (tears it down for * everyone) - do not route any of the three at another. * * The person removed cannot rejoin: the server bars them from the grace window everyone else * gets, so the UI must say so before calling this. `participantRef` is their FHIR reference, * which Chime carries as `externalUserId`. Removing yourself is refused (400) - use * `leaveSession`. */ removeParticipant(sessionId: string, participantRef: string): Promise; /** `POST /R4/video-session/{id}/share` - HOST ONLY. Shows one record item to the room, or stops * with `null`. Moves a reference only; every viewer reads the item under their own authorization. */ shareItem(sessionId: string, reference: string | null): Promise; /** `POST /R4/video-session/{id}/invite` - pulls one of the patient's collaboration teams into a visit * already in progress. A disclosure expansion, audited as one server-side (§5.5): practitioner * only, caller must already be a participant, team must be active or dormant. * * `teamId` IS REQUIRED (, Spencer's option (a)). A patient may own several * purpose-specific teams - cardiac, rehab, mental health - and the server used to CHOOSE one via a * GSI query with no tiebreaker, so a cardiac visit could notify and admit the mental-health team. * The caller names the team; omitting it is a 400, never a fallback to picking one. * * The server pins the named team to the encounter's subject patient before notifying anybody, and * refuses "not this patient's team" byte-identically to "no such team" - so a rejection here is * deliberately uninformative about which it was. Do not try to distinguish them client-side. */ inviteTeam(sessionId: string, teamId: string): Promise; /** The discovery repair: completes only the invitations this call already made, to their frozen * recipients. It names no team and selects none. */ repairDiscovery(sessionId: string): Promise; /** `GET /R4/video-session` (the COLLECTION route) - meetings this caller may join right now. * * It is deliberately NOT `GET /R4/video-session/available`. Lane A (§3.1): a literal * `/available` segment collides with the `/{id}` path parameter, and `routes.ts`'s own matcher * would plausibly resolve it to the `{id}` entry and apply **the wrong authorization config** - a * route that looks gated while running another route's gate. Collection-vs-item removes the * ambiguity by construction. If you see `/available` anywhere, it is stale. * * This is the OTHER half of the late-arriving patient: `call.available` reaches whoever was * already connected, this reaches whoever opened the portal afterwards. One affordance, two * transports - hence one shared `AvailableSession` shape. */ listAvailable(): Promise; /** `GET /R4/video-session/{id}` */ getSession(sessionId: string): Promise; }