@talkif/webrtc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +943 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +494 -0
- package/dist/index.d.ts +494 -0
- package/dist/index.js +908 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/data-channel.ts","../src/emitter.ts","../src/events.ts","../src/types.ts","../src/turnstile.ts","../src/signaling.ts","../src/call.ts"],"sourcesContent":["export { TalkifCall } from './call.js';\nexport { CallEventsClient } from './events.js';\nexport { PublicSession, SignalingClient, SignalingError } from './signaling.js';\nexport { TurnstileManager } from './turnstile.js';\nexport { isPublicConfig, TalkifCallError, TERMINAL_CALL_STATUSES } from './types.js';\nexport type {\n\tAppMessage,\n\tAuthProvider,\n\tCallEndReason,\n\tCallEventEnvelope,\n\tCallEventType,\n\tCallState,\n\tCallStatus,\n\tCreateCallRequest,\n\tCreateCallResponse,\n\tCreateTestCallRequest,\n\tIceServer,\n\tIceServersResponse,\n\tOfferRequest,\n\tOfferResponse,\n\tPeerLeftMessage,\n\tPublicSessionResponse,\n\tRenegotiateMessage,\n\tSignallingEnvelope,\n\tSignallingPayload,\n\tStartCallOptions,\n\tTalkifCallErrorCode,\n\tTalkifCallEventMap,\n\tTalkifClientConfig,\n\tTalkifConfig,\n\tTalkifPublicClientConfig,\n\tTrackStatusMessage,\n\tTtsChunkEvent,\n\tTtsWordEvent,\n\tWebRTCCall,\n} from './types.js';\n","import type { AppMessage, SignallingPayload, TrackStatusMessage } from './types.js';\n\nconst KEEPALIVE_INTERVAL_MS = 2_000;\n\nexport interface DataChannelHandlers {\n\tonSignalling: (message: SignallingPayload) => void;\n\tonAppMessage: (message: AppMessage) => void;\n}\n\n/**\n * Client side of the pipecat SmallWebRTC data-channel protocol:\n *\n * - `\"ping\"` strings every 2s keep the bot's liveness check happy (the bot\n * treats a connection as active when the last ping is <3s old, with the\n * ICE connection state as fallback).\n * - Inbound JSON is either `{type: \"signalling\", message: {...}}` (protocol)\n * or an arbitrary app message (forwarded to the consumer).\n */\nexport class DataChannel {\n\tprivate channel: RTCDataChannel;\n\tprivate keepalive: ReturnType<typeof setInterval> | null = null;\n\tprivate readonly handlers: DataChannelHandlers;\n\n\tconstructor(pc: RTCPeerConnection, handlers: DataChannelHandlers) {\n\t\tthis.handlers = handlers;\n\t\tthis.channel = pc.createDataChannel('pipecat', { ordered: true });\n\t\tthis.channel.onopen = () => this.startKeepalive();\n\t\tthis.channel.onclose = () => this.stopKeepalive();\n\t\tthis.channel.onmessage = (event) => this.handleMessage(event.data);\n\t}\n\n\tprivate startKeepalive(): void {\n\t\tthis.stopKeepalive();\n\t\tthis.keepalive = setInterval(() => {\n\t\t\tif (this.channel.readyState === 'open') {\n\t\t\t\tthis.channel.send('ping');\n\t\t\t}\n\t\t}, KEEPALIVE_INTERVAL_MS);\n\t}\n\n\tprivate stopKeepalive(): void {\n\t\tif (this.keepalive) {\n\t\t\tclearInterval(this.keepalive);\n\t\t\tthis.keepalive = null;\n\t\t}\n\t}\n\n\tprivate handleMessage(data: unknown): void {\n\t\tif (typeof data !== 'string') return;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(data);\n\t\t} catch {\n\t\t\treturn; // Non-JSON payloads (e.g. bot-side pings) carry no protocol meaning here.\n\t\t}\n\t\tif (typeof parsed !== 'object' || parsed === null) return;\n\n\t\tconst record = parsed as Record<string, unknown>;\n\t\tif (record['type'] === 'signalling' && typeof record['message'] === 'object' && record['message'] !== null) {\n\t\t\tthis.handlers.onSignalling(record['message'] as SignallingPayload);\n\t\t\treturn;\n\t\t}\n\t\tthis.handlers.onAppMessage(record as AppMessage);\n\t}\n\n\t/** Send an arbitrary JSON app message to the bot pipeline. */\n\tsendAppMessage(message: AppMessage): boolean {\n\t\tif (this.channel.readyState !== 'open') return false;\n\t\tthis.channel.send(JSON.stringify(message));\n\t\treturn true;\n\t}\n\n\t/** Tell the bot to enable/disable one of our media receivers. */\n\tsendTrackStatus(message: Omit<TrackStatusMessage, 'type'>): boolean {\n\t\tif (this.channel.readyState !== 'open') return false;\n\t\tthis.channel.send(JSON.stringify({ type: 'signalling', message: { type: 'trackStatus', ...message } }));\n\t\treturn true;\n\t}\n\n\tclose(): void {\n\t\tthis.stopKeepalive();\n\t\tthis.channel.onopen = null;\n\t\tthis.channel.onclose = null;\n\t\tthis.channel.onmessage = null;\n\t\tif (this.channel.readyState === 'open' || this.channel.readyState === 'connecting') {\n\t\t\tthis.channel.close();\n\t\t}\n\t}\n}\n","/** Minimal typed event emitter — no dependency, browser-safe. */\nexport class Emitter<Events extends object> {\n\tprivate listeners = new Map<keyof Events, Set<(payload: never) => void>>();\n\n\ton<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): () => void {\n\t\tlet set = this.listeners.get(event);\n\t\tif (!set) {\n\t\t\tset = new Set();\n\t\t\tthis.listeners.set(event, set);\n\t\t}\n\t\tset.add(listener as (payload: never) => void);\n\t\treturn () => this.off(event, listener);\n\t}\n\n\toff<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void {\n\t\tthis.listeners.get(event)?.delete(listener as (payload: never) => void);\n\t}\n\n\temit<K extends keyof Events>(event: K, payload: Events[K]): void {\n\t\tconst set = this.listeners.get(event);\n\t\tif (!set) return;\n\t\tfor (const listener of [...set]) {\n\t\t\t(listener as (payload: Events[K]) => void)(payload);\n\t\t}\n\t}\n\n\tremoveAll(): void {\n\t\tthis.listeners.clear();\n\t}\n}\n","import type { CallEventEnvelope } from './types.js';\n\n/** Reconnect backoff schedule (ms). Caps at the last entry. */\nconst BACKOFF_MS = [500, 1_000, 2_000, 4_000, 8_000];\n\nexport interface CallEventsOptions {\n\t/** API origin, e.g. `https://api.talkif.ai`. */\n\tbaseUrl: string;\n\t/** Fresh session token per (re)connect — first-message auth. */\n\tgetToken: () => Promise<string>;\n\t/** Every delivered envelope for the session's call. */\n\tonEvent: (event: CallEventEnvelope) => void;\n\t/**\n\t * A `seq` gap was observed: this connection shed frames under backpressure.\n\t * The client auto-requests a server-side replay on reconnect; consumers that\n\t * render transcripts incrementally may want to clear-and-rebuild on replay.\n\t */\n\tonGap?: (expected: number, received: number) => void;\n\t/** Optional WebSocket constructor override (tests, Node). */\n\twebSocket?: typeof WebSocket;\n}\n\n/**\n * Client for the public realtime events WebSocket\n * (`/api/v1/public/ws/events`).\n *\n * Protocol: connect → send `{\"type\":\"auth\",\"token\":...}` within 5s → send\n * `{\"type\":\"subscribe\"}` (the session is auto-bound to its one call) →\n * receive `{v, type, call_id?, seq, ts, data}` envelopes. Reconnects with\n * backoff; after the first successful connection, reconnect subscribes with\n * `replay: true` to recover anything missed during the gap.\n */\nexport class CallEventsClient {\n\tprivate readonly options: CallEventsOptions;\n\tprivate ws: WebSocket | null = null;\n\tprivate closed = false;\n\tprivate attempts = 0;\n\tprivate everConnected = false;\n\tprivate lastSeq = 0;\n\tprivate reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tconstructor(options: CallEventsOptions) {\n\t\tthis.options = options;\n\t}\n\n\tconnect(): void {\n\t\tif (this.closed || this.ws) return;\n\t\tvoid this.open();\n\t}\n\n\tclose(): void {\n\t\tthis.closed = true;\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\t\tthis.ws?.close(1000);\n\t\tthis.ws = null;\n\t}\n\n\tprivate wsUrl(): string {\n\t\tconst base = this.options.baseUrl.replace(/\\/$/, '').replace(/^http/, 'ws');\n\t\treturn `${base}/api/v1/public/ws/events`;\n\t}\n\n\tprivate async open(): Promise<void> {\n\t\tlet token: string;\n\t\ttry {\n\t\t\ttoken = await this.options.getToken();\n\t\t} catch {\n\t\t\tthis.scheduleReconnect();\n\t\t\treturn;\n\t\t}\n\t\tif (this.closed) return;\n\n\t\tconst WS = this.options.webSocket ?? WebSocket;\n\t\tconst ws = new WS(this.wsUrl());\n\t\tthis.ws = ws;\n\n\t\tws.onopen = () => {\n\t\t\tws.send(JSON.stringify({ type: 'auth', token }));\n\t\t\t// Auto-bound to the session's call; replay recovers a reconnect gap.\n\t\t\tws.send(\n\t\t\t\tJSON.stringify({ type: 'subscribe', ...(this.everConnected ? { replay: true } : {}) })\n\t\t\t);\n\t\t};\n\n\t\tws.onmessage = (message: MessageEvent) => {\n\t\t\tif (typeof message.data !== 'string') return;\n\t\t\tlet envelope: CallEventEnvelope;\n\t\t\ttry {\n\t\t\t\tenvelope = JSON.parse(message.data) as CallEventEnvelope;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (typeof envelope.type !== 'string' || typeof envelope.seq !== 'number') return;\n\n\t\t\tif (envelope.type === 'subscribed') {\n\t\t\t\tthis.everConnected = true;\n\t\t\t\tthis.attempts = 0;\n\t\t\t\tthis.lastSeq = envelope.seq;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (envelope.type === 'pong' || envelope.type === 'unsubscribed') {\n\t\t\t\tthis.lastSeq = envelope.seq;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.lastSeq > 0 && envelope.seq > this.lastSeq + 1) {\n\t\t\t\tthis.options.onGap?.(this.lastSeq + 1, envelope.seq);\n\t\t\t}\n\t\t\tthis.lastSeq = envelope.seq;\n\t\t\tthis.options.onEvent(envelope);\n\t\t};\n\n\t\tws.onclose = (event: CloseEvent) => {\n\t\t\tif (this.ws !== ws) return;\n\t\t\tthis.ws = null;\n\t\t\t// seq is per-connection — reset the tracker.\n\t\t\tthis.lastSeq = 0;\n\t\t\t// 4401/4403 are auth/scope rejections: retrying with the same session\n\t\t\t// can still succeed after a token refresh (getToken re-exchanges), but\n\t\t\t// a scope violation (4403) will never heal — stop there.\n\t\t\tif (event.code === 4403) {\n\t\t\t\tthis.closed = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.scheduleReconnect();\n\t\t};\n\n\t\tws.onerror = () => {\n\t\t\t// onclose follows and handles reconnection.\n\t\t};\n\t}\n\n\tprivate scheduleReconnect(): void {\n\t\tif (this.closed || this.reconnectTimer) return;\n\t\tconst delay = BACKOFF_MS[Math.min(this.attempts, BACKOFF_MS.length - 1)] ?? 8_000;\n\t\tthis.attempts += 1;\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\tthis.reconnectTimer = null;\n\t\t\tvoid this.open();\n\t\t}, delay);\n\t}\n}\n","/**\n * Wire types for the Talkif WebRTC signaling API and the pipecat SmallWebRTC\n * data-channel protocol spoken by Talkif voice bots.\n *\n * These shapes are the public contract. Field casing matches the backend JSON\n * exactly (camelCase over HTTP).\n */\n\n// ---------------------------------------------------------------------------\n// HTTP signaling\n// ---------------------------------------------------------------------------\n\nexport interface IceServer {\n\turls: string;\n\tusername?: string;\n\tcredential?: string;\n}\n\nexport interface IceServersResponse {\n\ticeServers: IceServer[];\n\t/** Seconds remaining on the underlying TURN credential. */\n\tttl: number;\n}\n\nexport interface CreateCallRequest {\n\tflowId: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface CreateTestCallRequest {\n\tflowId: string;\n\t/**\n\t * Draft flow definition (frontend `FlowDefinition` shape). The library does\n\t * not interpret it — it is passed through to the backend, which validates\n\t * and compiles it for the bot.\n\t */\n\tdefinition: Record<string, unknown>;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport type CallStatus =\n\t| 'CREATED'\n\t| 'BOT_ASSIGNED'\n\t| 'QUEUED'\n\t| 'RINGING'\n\t| 'IN_PROGRESS'\n\t| 'COMPLETED'\n\t| 'FAILED'\n\t| 'NO_ANSWER'\n\t| 'BUSY'\n\t| 'CANCELED';\n\n/** REST statuses that mean the call is genuinely over. */\nexport const TERMINAL_CALL_STATUSES: ReadonlySet<CallStatus> = new Set([\n\t'COMPLETED',\n\t'FAILED',\n\t'NO_ANSWER',\n\t'BUSY',\n\t'CANCELED',\n]);\n\nexport interface CreateCallResponse {\n\tcallId: string;\n\tflowId: string;\n\tstatus: CallStatus;\n\tbotId?: string | null;\n}\n\nexport interface WebRTCCall {\n\tcallId: string;\n\tstatus: CallStatus;\n}\n\nexport interface OfferRequest {\n\tsdp: string;\n\ticeServers?: IceServer[];\n}\n\n/** Public surface: token-exchange response (`POST /public/calls/session`). */\nexport interface PublicSessionResponse {\n\t/** Signed session token — sent as `Authorization: Bearer <token>`. */\n\ttoken: string;\n\t/** Seconds until the token expires (~15 min). */\n\texpiresIn: number;\n\t/** The flow this session may call (bound to the publishable key). */\n\tflowId: string;\n}\n\nexport interface OfferResponse {\n\tsdp: string;\n\tsdpType: string;\n\tbotId?: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Data-channel protocol (pipecat SmallWebRTC — bot side)\n// ---------------------------------------------------------------------------\n\n/** Bot → client: bot requests a new offer/answer cycle. */\nexport interface RenegotiateMessage {\n\ttype: 'renegotiate';\n}\n\n/** Bot → client: bot is closing the connection. */\nexport interface PeerLeftMessage {\n\ttype: 'peerLeft';\n}\n\n/** Client → bot: enable/disable a media receiver. 0=audio, 1=video, 2=screen. */\nexport interface TrackStatusMessage {\n\ttype: 'trackStatus';\n\treceiver_index: 0 | 1 | 2;\n\tenabled: boolean;\n}\n\nexport type SignallingPayload = RenegotiateMessage | PeerLeftMessage | TrackStatusMessage;\n\nexport interface SignallingEnvelope {\n\ttype: 'signalling';\n\tmessage: SignallingPayload;\n}\n\n/**\n * Anything on the data channel that is not the signalling envelope (and not a\n * `\"ping\"` keepalive string) is an app message: arbitrary JSON exchanged with\n * the bot pipeline.\n */\nexport type AppMessage = Record<string, unknown>;\n\n// ---------------------------------------------------------------------------\n// Realtime events WebSocket (`/api/v1/public/ws/events`)\n// ---------------------------------------------------------------------------\n\n/**\n * Server envelope on the realtime events WebSocket. `seq` is per-connection\n * and monotonic — a gap means THIS connection dropped frames under\n * backpressure, not that the call missed anything.\n */\nexport interface CallEventEnvelope {\n\tv: number;\n\ttype: string;\n\tcall_id?: string;\n\tseq: number;\n\tts: number;\n\tdata: Record<string, unknown>;\n}\n\n/** Event types the public WS delivers for the session's call. */\nexport type CallEventType =\n\t| 'transcript'\n\t| 'interim'\n\t| 'status'\n\t| 'tts'\n\t| 'tts_word'\n\t| 'speech'\n\t| 'turn'\n\t| 'node_transition'\n\t| 'error';\n\n// ---------------------------------------------------------------------------\n// Client state machine & events\n// ---------------------------------------------------------------------------\n\nexport type CallState =\n\t| 'idle'\n\t| 'requesting'\n\t| 'connecting'\n\t| 'connected'\n\t| 'ended'\n\t| 'error';\n\nexport interface TalkifCallEventMap {\n\t/** State machine transition. */\n\tstatechange: { state: CallState; previous: CallState };\n\t/** Media is flowing and the SDP answer is applied. */\n\tconnected: { callId: string; botId: string | null };\n\t/** Remote (bot) audio track arrived. */\n\ttrack: { stream: MediaStream };\n\t/** Call finished normally (user hangup, bot peerLeft, terminal status). */\n\tended: { reason: CallEndReason };\n\t/** Fatal failure. The call is torn down; state is 'error'. */\n\terror: { error: TalkifCallError };\n\t/** One-second duration tick while connected. */\n\ttick: { durationSecs: number };\n\t/** JSON app message from the bot over the data channel. */\n\tappmessage: { message: AppMessage };\n\t/** Bot asked for renegotiation. Emitted after the re-offer is attempted. */\n\trenegotiated: { ok: boolean };\n\t/** Mute toggled. */\n\tmutechange: { muted: boolean };\n\t/**\n\t * Any realtime event for this call from the events WebSocket (public mode).\n\t * Fires for every delivered envelope, including those with dedicated\n\t * events below.\n\t */\n\tcallevent: { event: CallEventEnvelope };\n\t/** Final transcript line (public mode, via the events WebSocket). */\n\ttranscript: { role: string; content: string; data: Record<string, unknown> };\n\t/** Interim (in-progress) transcription (public mode). May be dropped under backpressure. */\n\tinterim: { data: Record<string, unknown> };\n\t/**\n\t * Word-level TTS timing (public mode). Drives karaoke-style caption\n\t * highlighting; only words actually spoken are delivered. May be dropped\n\t * under backpressure.\n\t */\n\tttsword: TtsWordEvent;\n\t/**\n\t * Sentence-level chunk of the agent's reply (public mode), emitted when the\n\t * LLM output is handed to TTS synthesis — ahead of audio playback. Use for\n\t * chat-style streaming of the reply; reconcile with the final `transcript`\n\t * (on interruption the tail may never be spoken). May be dropped under\n\t * backpressure.\n\t */\n\tttschunk: TtsChunkEvent;\n}\n\n/** Payload of a `tts` realtime event. */\nexport interface TtsChunkEvent {\n\t/** Sentence-level text chunk of the agent's reply. */\n\ttext: string;\n\t/** Unix timestamp from the bot (seconds, fractional). */\n\ttimestamp: number;\n\t/** Full raw event payload (character counts, TTS timing metrics…). */\n\tdata: Record<string, unknown>;\n}\n\n/** Payload of a `tts_word` realtime event. */\nexport interface TtsWordEvent {\n\t/** The spoken word. */\n\tword: string;\n\t/** Presentation timestamp — when the word is spoken in the audio (ms), if known. */\n\tptsMs: number | null;\n\t/** Unix timestamp from the bot (seconds, fractional). */\n\ttimestamp: number;\n\t/** Full raw event payload. */\n\tdata: Record<string, unknown>;\n}\n\nexport type CallEndReason =\n\t| 'local-hangup'\n\t| 'peer-left'\n\t| 'terminal-status'\n\t| 'external';\n\nexport type TalkifCallErrorCode =\n\t| 'media-unavailable'\n\t| 'signaling-failed'\n\t| 'connection-failed'\n\t| 'pipeline-dead'\n\t| 'renegotiation-failed'\n\t/** Publishable key rejected, origin not allowed, or session expired. */\n\t| 'session-rejected'\n\t/** This session already has an active call (multi-tab). */\n\t| 'call-active'\n\t/** Rate/concurrency limit hit — retry later. */\n\t| 'rate-limited'\n\t/** No bot available right now — retry shortly. */\n\t| 'no-agent';\n\nexport class TalkifCallError extends Error {\n\treadonly code: TalkifCallErrorCode;\n\toverride readonly cause?: unknown;\n\n\tconstructor(code: TalkifCallErrorCode, message: string, cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = 'TalkifCallError';\n\t\tthis.code = code;\n\t\tthis.cause = cause;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Supplies the Authorization header value for signaling requests.\n * Called per request so short-lived tokens (JWT refresh, widget session\n * tokens) stay current. Return e.g. `\"Bearer <token>\"`.\n */\nexport type AuthProvider = () => string | Promise<string>;\n\nexport interface TalkifClientConfig {\n\t/** API origin, e.g. `https://api.talkif.ai`. No trailing slash. */\n\tbaseUrl: string;\n\taccountId: string;\n\tauth: AuthProvider;\n\t/** Override fetch (tests, SSR polyfills). Defaults to global fetch. */\n\tfetch?: typeof fetch;\n}\n\n/**\n * Public (embeddable) configuration: the browser-safe publishable key from the\n * flow's public-access settings. The library exchanges it for a short-lived\n * session token itself — no bearer token, no account id. The page's Origin\n * must be on the key's allowlist.\n */\nexport interface TalkifPublicClientConfig {\n\t/** API origin, e.g. `https://api.talkif.ai`. No trailing slash. */\n\tbaseUrl: string;\n\t/** Publishable key (`pk_live_...`) — safe to ship in page source. */\n\tpublishableKey: string;\n\t/**\n\t * Cloudflare Turnstile sitekey (Talkif's — public, safe to ship). When set,\n\t * the library renders an invisible Turnstile challenge and produces a token\n\t * for each session exchange automatically. Leave unset to disable the gate\n\t * (dev/local, or before Turnstile is provisioned).\n\t */\n\tturnstileSiteKey?: string;\n\t/**\n\t * Manual Turnstile token supplier. Overrides `turnstileSiteKey` — supply\n\t * this only if you render Turnstile yourself. Called at each exchange.\n\t */\n\tturnstileToken?: () => string | Promise<string>;\n\t/** Override fetch (tests, SSR polyfills). Defaults to global fetch. */\n\tfetch?: typeof fetch;\n}\n\nexport type TalkifConfig = TalkifClientConfig | TalkifPublicClientConfig;\n\nexport function isPublicConfig(config: TalkifConfig): config is TalkifPublicClientConfig {\n\treturn 'publishableKey' in config;\n}\n\nexport interface StartCallOptions {\n\t/**\n\t * Start a call against a published flow. Required in authenticated mode.\n\t * Ignored in public mode — the flow is bound to the publishable key.\n\t */\n\tflowId?: string;\n\t/**\n\t * When set, uses the draft test-call endpoint with this definition instead\n\t * of the published flow.\n\t */\n\tdraftDefinition?: Record<string, unknown>;\n\tmetadata?: Record<string, unknown>;\n\t/** Constraints for getUserMedia. Defaults to `{ audio: true }`. */\n\taudio?: MediaTrackConstraints | boolean;\n\t/**\n\t * Attach remote audio to this element instead of an internally created\n\t * autoplay `Audio()`. Pass `null` to disable playback entirely (consumer\n\t * handles the `track` event itself).\n\t */\n\taudioElement?: HTMLAudioElement | null;\n}\n","/**\n * Cloudflare Turnstile integration — the \"prove you're a real browser\" gate\n * that stops scripted abuse of the public call surface.\n *\n * The customer does nothing: the widget self-injects Cloudflare's script,\n * renders an invisible Turnstile widget with Talkif's sitekey, and produces a\n * fresh token for each session exchange. The sitekey is public and safe to\n * ship; the matching secret lives only in the backend.\n */\n\nconst TURNSTILE_SCRIPT_URL =\n\t'https://challenges.cloudflare.com/turnstile/v0/api.js';\n\n/** Minimal shape of the global `turnstile` object we use. */\ninterface TurnstileApi {\n\trender(\n\t\tcontainer: HTMLElement,\n\t\toptions: {\n\t\t\tsitekey: string;\n\t\t\tcallback?: (token: string) => void;\n\t\t\t'error-callback'?: (code?: string) => void;\n\t\t\t'expired-callback'?: () => void;\n\t\t\tsize?: 'invisible' | 'normal' | 'compact' | 'flexible';\n\t\t\texecution?: 'render' | 'execute';\n\t\t\tretry?: 'auto' | 'never';\n\t\t}\n\t): string;\n\texecute(widgetId: string, options?: { sitekey?: string }): void;\n\treset(widgetId: string): void;\n\tremove(widgetId: string): void;\n}\n\ndeclare global {\n\tinterface Window {\n\t\tturnstile?: TurnstileApi;\n\t}\n}\n\nlet scriptPromise: Promise<void> | null = null;\n\n/** Load Cloudflare's Turnstile script once, shared across all instances. */\nfunction loadScript(): Promise<void> {\n\tif (typeof document === 'undefined') {\n\t\treturn Promise.reject(new Error('Turnstile requires a browser environment'));\n\t}\n\tif (window.turnstile) return Promise.resolve();\n\tscriptPromise ??= new Promise<void>((resolve, reject) => {\n\t\tconst existing = document.querySelector<HTMLScriptElement>(\n\t\t\t`script[src=\"${TURNSTILE_SCRIPT_URL}\"]`\n\t\t);\n\t\tif (existing) {\n\t\t\texisting.addEventListener('load', () => resolve());\n\t\t\texisting.addEventListener('error', () => reject(new Error('Turnstile script failed to load')));\n\t\t\tif (window.turnstile) resolve();\n\t\t\treturn;\n\t\t}\n\t\tconst script = document.createElement('script');\n\t\tscript.src = TURNSTILE_SCRIPT_URL;\n\t\tscript.async = true;\n\t\tscript.defer = true;\n\t\tscript.onload = () => resolve();\n\t\tscript.onerror = () => reject(new Error('Turnstile script failed to load'));\n\t\tdocument.head.appendChild(script);\n\t});\n\treturn scriptPromise;\n}\n\n/**\n * Manages one invisible Turnstile widget and hands out fresh tokens.\n *\n * Turnstile tokens are single-use and short-lived, so we `execute()` a fresh\n * challenge for each call to `getToken()` (i.e. each session exchange). The\n * widget is rendered lazily on first use and torn down with `dispose()`.\n */\nexport class TurnstileManager {\n\tprivate readonly siteKey: string;\n\tprivate container: HTMLDivElement | null = null;\n\tprivate widgetId: string | null = null;\n\tprivate pending: {\n\t\tresolve: (token: string) => void;\n\t\treject: (err: Error) => void;\n\t} | null = null;\n\n\tconstructor(siteKey: string) {\n\t\tthis.siteKey = siteKey;\n\t}\n\n\t/** Obtain a fresh Turnstile token. Rejects if the challenge fails. */\n\tasync getToken(): Promise<string> {\n\t\tawait loadScript();\n\t\tconst turnstile = window.turnstile;\n\t\tif (!turnstile) throw new Error('Turnstile unavailable after script load');\n\n\t\tif (!this.container) {\n\t\t\t// A hidden, off-screen host — the invisible widget never shows UI\n\t\t\t// but Cloudflare still requires an element to render into.\n\t\t\tthis.container = document.createElement('div');\n\t\t\tthis.container.style.position = 'fixed';\n\t\t\tthis.container.style.width = '0';\n\t\t\tthis.container.style.height = '0';\n\t\t\tthis.container.style.overflow = 'hidden';\n\t\t\tthis.container.style.pointerEvents = 'none';\n\t\t\tthis.container.setAttribute('aria-hidden', 'true');\n\t\t\tdocument.body.appendChild(this.container);\n\t\t}\n\n\t\tif (this.widgetId === null) {\n\t\t\tthis.widgetId = turnstile.render(this.container, {\n\t\t\t\tsitekey: this.siteKey,\n\t\t\t\tsize: 'invisible',\n\t\t\t\texecution: 'execute',\n\t\t\t\tretry: 'auto',\n\t\t\t\tcallback: (token: string) => {\n\t\t\t\t\tthis.pending?.resolve(token);\n\t\t\t\t\tthis.pending = null;\n\t\t\t\t},\n\t\t\t\t'error-callback': (code?: string) => {\n\t\t\t\t\tthis.pending?.reject(new Error(`Turnstile challenge failed${code ? `: ${code}` : ''}`));\n\t\t\t\t\tthis.pending = null;\n\t\t\t\t},\n\t\t\t\t'expired-callback': () => {\n\t\t\t\t\t// Token expired before use — the next getToken() re-executes.\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn new Promise<string>((resolve, reject) => {\n\t\t\t// Only one outstanding challenge at a time; supersede any prior.\n\t\t\tif (this.pending) {\n\t\t\t\tthis.pending.reject(new Error('Turnstile challenge superseded'));\n\t\t\t}\n\t\t\tthis.pending = { resolve, reject };\n\t\t\tconst id = this.widgetId;\n\t\t\tif (id === null) {\n\t\t\t\treject(new Error('Turnstile widget not rendered'));\n\t\t\t\tthis.pending = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tturnstile.reset(id);\n\t\t\t\tturnstile.execute(id, { sitekey: this.siteKey });\n\t\t\t} catch (err) {\n\t\t\t\tthis.pending = null;\n\t\t\t\treject(err instanceof Error ? err : new Error('Turnstile execute failed'));\n\t\t\t}\n\t\t});\n\t}\n\n\tdispose(): void {\n\t\tif (this.widgetId !== null && window.turnstile) {\n\t\t\ttry {\n\t\t\t\twindow.turnstile.remove(this.widgetId);\n\t\t\t} catch {\n\t\t\t\t// best-effort\n\t\t\t}\n\t\t}\n\t\tthis.widgetId = null;\n\t\tthis.pending = null;\n\t\tif (this.container) {\n\t\t\tthis.container.remove();\n\t\t\tthis.container = null;\n\t\t}\n\t}\n}\n","import type {\n\tCreateCallRequest,\n\tCreateCallResponse,\n\tCreateTestCallRequest,\n\tIceServersResponse,\n\tOfferRequest,\n\tOfferResponse,\n\tPublicSessionResponse,\n\tTalkifConfig,\n\tTalkifPublicClientConfig,\n\tWebRTCCall,\n} from './types.js';\nimport { isPublicConfig, TalkifCallError } from './types.js';\nimport { TurnstileManager } from './turnstile.js';\n\nexport class SignalingError extends Error {\n\treadonly status: number;\n\treadonly body: unknown;\n\n\tconstructor(status: number, message: string, body: unknown) {\n\t\tsuper(message);\n\t\tthis.name = 'SignalingError';\n\t\tthis.status = status;\n\t\tthis.body = body;\n\t}\n}\n\n/** Re-exchange the publishable key this many ms before the token expires. */\nconst SESSION_REFRESH_MARGIN_MS = 60_000;\n\n/**\n * Holds the short-lived session token for the public surface: exchanges the\n * publishable key on first use, re-exchanges near expiry, and can be\n * invalidated on a 401 so the next request retries with a fresh token.\n */\nexport class PublicSession {\n\tprivate readonly config: TalkifPublicClientConfig;\n\tprivate token: string | null = null;\n\tprivate expiresAt = 0;\n\tprivate flowIdValue: string | null = null;\n\tprivate inflight: Promise<string> | null = null;\n\tprivate turnstile: TurnstileManager | null = null;\n\n\tconstructor(config: TalkifPublicClientConfig) {\n\t\tthis.config = config;\n\t\t// Built-in Turnstile: render an invisible challenge from the sitekey,\n\t\t// unless the caller supplies their own token provider.\n\t\tif (config.turnstileSiteKey && !config.turnstileToken) {\n\t\t\tthis.turnstile = new TurnstileManager(config.turnstileSiteKey);\n\t\t}\n\t}\n\n\t/** A fresh Turnstile token, from the manual supplier or the built-in\n\t * manager; undefined when no gate is configured (dev/local). */\n\tprivate async turnstileToken(): Promise<string | undefined> {\n\t\tif (this.config.turnstileToken) {\n\t\t\treturn this.config.turnstileToken();\n\t\t}\n\t\tif (this.turnstile) {\n\t\t\treturn this.turnstile.getToken();\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Tear down the Turnstile widget (call when the client is disposed). */\n\tdispose(): void {\n\t\tthis.turnstile?.dispose();\n\t\tthis.turnstile = null;\n\t}\n\n\t/** Flow bound to the publishable key (known after the first exchange). */\n\tget flowId(): string | null {\n\t\treturn this.flowIdValue;\n\t}\n\n\tasync getToken(): Promise<string> {\n\t\tif (this.token && Date.now() < this.expiresAt - SESSION_REFRESH_MARGIN_MS) {\n\t\t\treturn this.token;\n\t\t}\n\t\t// Single-flight the exchange: concurrent requests share one round-trip.\n\t\tthis.inflight ??= this.exchange().finally(() => {\n\t\t\tthis.inflight = null;\n\t\t});\n\t\treturn this.inflight;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.token = null;\n\t\tthis.expiresAt = 0;\n\t}\n\n\tprivate async exchange(): Promise<string> {\n\t\tconst doFetch = this.config.fetch ?? fetch;\n\t\tconst turnstileToken = await this.turnstileToken();\n\t\tconst response = await doFetch(\n\t\t\t`${this.config.baseUrl.replace(/\\/$/, '')}/api/v1/public/calls/session`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tpublishableKey: this.config.publishableKey,\n\t\t\t\t\t...(turnstileToken !== undefined ? { turnstileToken } : {}),\n\t\t\t\t}),\n\t\t\t}\n\t\t);\n\t\tconst parsed: unknown = await response.json().catch(() => null);\n\t\tif (!response.ok) {\n\t\t\tthrow new SignalingError(\n\t\t\t\tresponse.status,\n\t\t\t\t'Session exchange failed — check the publishable key and that this origin is on its allowlist',\n\t\t\t\tparsed\n\t\t\t);\n\t\t}\n\t\tconst session = parsed as PublicSessionResponse;\n\t\tthis.token = session.token;\n\t\tthis.expiresAt = Date.now() + session.expiresIn * 1_000;\n\t\tthis.flowIdValue = session.flowId;\n\t\treturn session.token;\n\t}\n}\n\n/**\n * Thin HTTP client for the Talkif WebRTC signaling routes. Two modes:\n * - authenticated (dashboard/API): `/api/v1/accounts/{accountId}/calls/webrtc`\n * - public (embed): `/api/v1/public/calls`, session token minted from the\n * publishable key and refreshed transparently.\n */\nexport class SignalingClient {\n\tprivate readonly config: TalkifConfig;\n\treadonly session: PublicSession | null;\n\n\tconstructor(config: TalkifConfig) {\n\t\tthis.config = config;\n\t\tthis.session = isPublicConfig(config) ? new PublicSession(config) : null;\n\t}\n\n\tget isPublic(): boolean {\n\t\treturn this.session !== null;\n\t}\n\n\tprivate get base(): string {\n\t\tconst baseUrl = this.config.baseUrl.replace(/\\/$/, '');\n\t\tif (isPublicConfig(this.config)) {\n\t\t\treturn `${baseUrl}/api/v1/public/calls`;\n\t\t}\n\t\treturn `${baseUrl}/api/v1/accounts/${this.config.accountId}/calls/webrtc`;\n\t}\n\n\tprivate async authorization(): Promise<string> {\n\t\tif (this.session) {\n\t\t\treturn `Bearer ${await this.session.getToken()}`;\n\t\t}\n\t\treturn (this.config as { auth: () => string | Promise<string> }).auth();\n\t}\n\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST',\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\tretriedAuth = false\n\t): Promise<T> {\n\t\tconst doFetch = this.config.fetch ?? fetch;\n\t\tconst authorization = await this.authorization();\n\t\tconst response = await doFetch(`${this.base}${path}`, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\tauthorization,\n\t\t\t\t...(body !== undefined ? { 'content-type': 'application/json' } : {}),\n\t\t\t},\n\t\t\t...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n\t\t});\n\n\t\t// Public mode: an expired/rotated session token gets one transparent\n\t\t// re-exchange + retry before surfacing the failure.\n\t\tif (response.status === 401 && this.session && !retriedAuth) {\n\t\t\tthis.session.invalidate();\n\t\t\treturn this.request(method, path, body, true);\n\t\t}\n\n\t\tlet parsed: unknown = null;\n\t\tconst text = await response.text();\n\t\tif (text) {\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(text);\n\t\t\t} catch {\n\t\t\t\tparsed = text;\n\t\t\t}\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst message =\n\t\t\t\ttypeof parsed === 'object' && parsed !== null && 'detail' in parsed\n\t\t\t\t\t? String((parsed as { detail: unknown }).detail)\n\t\t\t\t\t: typeof parsed === 'object' && parsed !== null && 'message' in parsed\n\t\t\t\t\t\t? String((parsed as { message: unknown }).message)\n\t\t\t\t\t\t: `Signaling request failed: ${method} ${path} → ${response.status}`;\n\t\t\tthrow new SignalingError(response.status, message, parsed);\n\t\t}\n\t\treturn parsed as T;\n\t}\n\n\tcreateCall(body: CreateCallRequest): Promise<CreateCallResponse> {\n\t\tif (this.isPublic) {\n\t\t\t// Flow is bound to the publishable key server-side; body is empty.\n\t\t\treturn this.request('POST', '/calls', {});\n\t\t}\n\t\treturn this.request('POST', '', body);\n\t}\n\n\tcreateTestCall(body: CreateTestCallRequest): Promise<CreateCallResponse> {\n\t\tif (this.isPublic) {\n\t\t\tthrow new TalkifCallError(\n\t\t\t\t'signaling-failed',\n\t\t\t\t'Draft test calls are not available on the public surface'\n\t\t\t);\n\t\t}\n\t\treturn this.request('POST', '/test', body);\n\t}\n\n\tgetIceServers(): Promise<IceServersResponse> {\n\t\treturn this.request('GET', '/ice-servers');\n\t}\n\n\tsendOffer(callId: string, body: OfferRequest): Promise<OfferResponse> {\n\t\tconst path = this.isPublic ? `/calls/${callId}/offer` : `/${callId}/offer`;\n\t\treturn this.request('POST', path, body);\n\t}\n\n\tgetCall(callId: string): Promise<WebRTCCall> {\n\t\tconst path = this.isPublic ? `/calls/${callId}` : `/${callId}`;\n\t\treturn this.request('GET', path);\n\t}\n}\n\n/** Map a signaling failure to the library's typed error. */\nexport function asSignalingCallError(error: unknown): TalkifCallError {\n\tif (error instanceof TalkifCallError) return error;\n\tif (error instanceof SignalingError) {\n\t\tconst code =\n\t\t\terror.status === 401\n\t\t\t\t? 'session-rejected'\n\t\t\t\t: error.status === 409\n\t\t\t\t\t? 'call-active'\n\t\t\t\t\t: error.status === 429\n\t\t\t\t\t\t? 'rate-limited'\n\t\t\t\t\t\t: error.status === 503\n\t\t\t\t\t\t\t? 'no-agent'\n\t\t\t\t\t\t\t: 'signaling-failed';\n\t\treturn new TalkifCallError(code, error.message, error);\n\t}\n\tconst message = error instanceof Error ? error.message : 'Signaling request failed';\n\treturn new TalkifCallError('signaling-failed', message, error);\n}\n","import { DataChannel } from './data-channel.js';\nimport { Emitter } from './emitter.js';\nimport { CallEventsClient } from './events.js';\nimport { asSignalingCallError, SignalingClient } from './signaling.js';\nimport type {\n\tAppMessage,\n\tCallEndReason,\n\tCallEventEnvelope,\n\tCallState,\n\tIceServer,\n\tSignallingPayload,\n\tStartCallOptions,\n\tTalkifCallEventMap,\n\tTalkifConfig,\n} from './types.js';\nimport { TalkifCallError, TERMINAL_CALL_STATUSES } from './types.js';\n\n// If no external health confirmation (SSE/app message) arrives within this\n// window after media connects, verify call health against the backend over\n// REST before tearing down. Silence alone is NOT proof of pipeline death.\nconst PIPELINE_HEALTH_TIMEOUT_MS = 15_000;\n\n// Safety net for ICE gathering: send whatever we have if no relay candidate\n// shows up. With iceTransportPolicy 'relay' the first relay(udp) candidate is\n// all the bot needs; TURN tcp/443 allocations routinely never complete, so\n// waiting for iceGatheringState === 'complete' would stall every call.\nconst ICE_FIRST_RELAY_TIMEOUT_MS = 1_500;\n\n/**\n * One WebRTC voice session with a Talkif agent.\n *\n * Lifecycle: `idle → requesting → connecting → connected → ended | error`.\n * A TalkifCall instance is single-use: create a new one per call.\n */\nexport class TalkifCall {\n\tprivate readonly signaling: SignalingClient;\n\tprivate readonly config: TalkifConfig;\n\tprivate readonly emitter = new Emitter<TalkifCallEventMap>();\n\tprivate events: CallEventsClient | null = null;\n\n\tprivate pc: RTCPeerConnection | null = null;\n\tprivate localStream: MediaStream | null = null;\n\tprivate remoteAudio: HTMLAudioElement | null = null;\n\tprivate ownsAudioElement = false;\n\tprivate dataChannel: DataChannel | null = null;\n\tprivate iceServers: IceServer[] = [];\n\n\tprivate durationTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate healthTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate pipelineConfirmed = false;\n\tprivate renegotiating = false;\n\n\tprivate _state: CallState = 'idle';\n\tprivate _callId: string | null = null;\n\tprivate _botId: string | null = null;\n\tprivate _muted = false;\n\tprivate _durationSecs = 0;\n\n\tconstructor(config: TalkifConfig) {\n\t\tthis.config = config;\n\t\tthis.signaling = new SignalingClient(config);\n\t}\n\n\t// -- public surface -------------------------------------------------------\n\n\tget state(): CallState {\n\t\treturn this._state;\n\t}\n\n\tget callId(): string | null {\n\t\treturn this._callId;\n\t}\n\n\tget botId(): string | null {\n\t\treturn this._botId;\n\t}\n\n\tget muted(): boolean {\n\t\treturn this._muted;\n\t}\n\n\tget durationSecs(): number {\n\t\treturn this._durationSecs;\n\t}\n\n\ton<K extends keyof TalkifCallEventMap>(\n\t\tevent: K,\n\t\tlistener: (payload: TalkifCallEventMap[K]) => void\n\t): () => void {\n\t\treturn this.emitter.on(event, listener);\n\t}\n\n\toff<K extends keyof TalkifCallEventMap>(\n\t\tevent: K,\n\t\tlistener: (payload: TalkifCallEventMap[K]) => void\n\t): void {\n\t\tthis.emitter.off(event, listener);\n\t}\n\n\t/**\n\t * External health signal (e.g. the dashboard's SSE layer saw a\n\t * RUNNING/CONNECTED status). Cancels the REST health watchdog.\n\t */\n\tconfirmPipelineHealthy(): void {\n\t\tthis.pipelineConfirmed = true;\n\t\tif (this.healthTimer) {\n\t\t\tclearTimeout(this.healthTimer);\n\t\t\tthis.healthTimer = null;\n\t\t}\n\t}\n\n\t/**\n\t * External terminal signal (e.g. SSE reported COMPLETED). Tears down and\n\t * transitions to 'ended'.\n\t */\n\tendedExternally(): void {\n\t\tif (this._state !== 'connecting' && this._state !== 'connected') return;\n\t\tthis.teardown();\n\t\tthis.setState('ended');\n\t\tthis.emitter.emit('ended', { reason: 'external' });\n\t}\n\n\t/** Send an arbitrary JSON app message to the bot over the data channel. */\n\tsendAppMessage(message: AppMessage): boolean {\n\t\treturn this.dataChannel?.sendAppMessage(message) ?? false;\n\t}\n\n\tsetMuted(muted: boolean): void {\n\t\tif (!this.localStream) return;\n\t\tfor (const track of this.localStream.getAudioTracks()) {\n\t\t\ttrack.enabled = !muted;\n\t\t}\n\t\tthis._muted = muted;\n\t\tthis.emitter.emit('mutechange', { muted });\n\t}\n\n\t/** User-initiated hangup. */\n\thangup(): void {\n\t\tif (this._state === 'idle' || this._state === 'ended' || this._state === 'error') return;\n\t\tthis.teardown();\n\t\tthis.setState('ended');\n\t\tthis.emitter.emit('ended', { reason: 'local-hangup' });\n\t}\n\n\t/** Full teardown regardless of state — safe to call multiple times. */\n\tdispose(): void {\n\t\tthis.teardown();\n\t\tthis.signaling.session?.dispose();\n\t\tthis.emitter.removeAll();\n\t}\n\n\t// -- lifecycle ------------------------------------------------------------\n\n\tasync start(options: StartCallOptions): Promise<void> {\n\t\tif (this._state !== 'idle') {\n\t\t\tthrow new TalkifCallError('signaling-failed', `Cannot start a call from state '${this._state}' — create a new TalkifCall`);\n\t\t}\n\t\tthis.setState('requesting');\n\n\t\tif (!this.signaling.isPublic && !options.flowId) {\n\t\t\tthrow new TalkifCallError('signaling-failed', 'flowId is required in authenticated mode');\n\t\t}\n\n\t\ttry {\n\t\t\t// Run bot warmup (create call → bot assignment, 2-5s) in parallel with\n\t\t\t// local setup (ICE fetch, mic, offer, candidate gathering).\n\t\t\tconst flowId = options.flowId ?? '';\n\t\t\tconst [callResponse, local] = await Promise.all([\n\t\t\t\toptions.draftDefinition !== undefined\n\t\t\t\t\t? this.signaling.createTestCall({\n\t\t\t\t\t\t\tflowId,\n\t\t\t\t\t\t\tdefinition: options.draftDefinition,\n\t\t\t\t\t\t\t...(options.metadata !== undefined ? { metadata: options.metadata } : {}),\n\t\t\t\t\t\t})\n\t\t\t\t\t: this.signaling.createCall({\n\t\t\t\t\t\t\tflowId,\n\t\t\t\t\t\t\t...(options.metadata !== undefined ? { metadata: options.metadata } : {}),\n\t\t\t\t\t\t}),\n\t\t\t\tthis.prepareLocal(options),\n\t\t\t]);\n\n\t\t\tthis._callId = callResponse.callId;\n\t\t\tthis.pc = local.pc;\n\t\t\tthis.localStream = local.stream;\n\t\t\tthis.setState('connecting');\n\n\t\t\tthis.pc.ontrack = (event) => {\n\t\t\t\tconst stream = event.streams[0];\n\t\t\t\tif (!stream) return;\n\t\t\t\tthis.attachRemoteAudio(stream, options);\n\t\t\t\tthis.emitter.emit('track', { stream });\n\t\t\t};\n\n\t\t\tthis.pc.onconnectionstatechange = () => {\n\t\t\t\tconst state = this.pc?.connectionState;\n\t\t\t\tif (state === 'failed' || state === 'disconnected') {\n\t\t\t\t\tthis.fail(new TalkifCallError('connection-failed', 'WebRTC connection failed'));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.dataChannel = new DataChannel(this.pc, {\n\t\t\t\tonSignalling: (message) => this.handleSignalling(message),\n\t\t\t\tonAppMessage: (message) => this.emitter.emit('appmessage', { message }),\n\t\t\t});\n\n\t\t\tconst answer = await this.signaling.sendOffer(callResponse.callId, {\n\t\t\t\tsdp: this.mustLocalSdp(),\n\t\t\t\ticeServers: this.iceServers,\n\t\t\t});\n\t\t\tawait this.pc.setRemoteDescription({ type: 'answer', sdp: answer.sdp });\n\t\t\tthis._botId = answer.botId ?? null;\n\n\t\t\tthis.setState('connected');\n\t\t\tthis.emitter.emit('connected', { callId: callResponse.callId, botId: this._botId });\n\t\t\tthis.startDurationTimer();\n\t\t\tthis.armHealthWatchdog(callResponse.callId);\n\t\t\tthis.connectEvents();\n\t\t} catch (error) {\n\t\t\tthis.fail(\n\t\t\t\terror instanceof TalkifCallError ? error : asSignalingCallError(error)\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async prepareLocal(options: StartCallOptions): Promise<{ pc: RTCPeerConnection; stream: MediaStream }> {\n\t\tconst { iceServers } = await this.signaling.getIceServers();\n\t\tthis.iceServers = iceServers;\n\n\t\tif (!navigator.mediaDevices?.getUserMedia) {\n\t\t\tthrow new TalkifCallError('media-unavailable', 'Microphone access unavailable. This requires HTTPS or localhost.');\n\t\t}\n\t\tconst stream = await navigator.mediaDevices\n\t\t\t.getUserMedia({ audio: options.audio ?? true })\n\t\t\t.catch((cause: unknown) => {\n\t\t\t\tthrow new TalkifCallError('media-unavailable', 'Microphone permission denied or unavailable', cause);\n\t\t\t});\n\n\t\t// Relay-only transport: skips host/srflx gathering (no dead-interface\n\t\t// timeouts), works through firewalls that block UDP, avoids TURN\n\t\t// \"Forbidden IP\" errors for private IPs, and gathers in ~100-200ms.\n\t\tconst pc = new RTCPeerConnection({\n\t\t\ticeServers: iceServers.map((s) => ({\n\t\t\t\turls: s.urls,\n\t\t\t\t...(s.username !== undefined ? { username: s.username } : {}),\n\t\t\t\t...(s.credential !== undefined ? { credential: s.credential } : {}),\n\t\t\t})),\n\t\t\ticeTransportPolicy: 'relay',\n\t\t});\n\t\tfor (const track of stream.getTracks()) {\n\t\t\tpc.addTrack(track, stream);\n\t\t}\n\n\t\tconst offer = await pc.createOffer();\n\t\tawait pc.setLocalDescription(offer);\n\t\tif (!offer.sdp) {\n\t\t\tthrow new TalkifCallError('signaling-failed', 'Failed to create SDP offer');\n\t\t}\n\n\t\tawait this.waitForFirstRelayCandidate(pc);\n\t\treturn { pc, stream };\n\t}\n\n\tprivate waitForFirstRelayCandidate(pc: RTCPeerConnection): Promise<void> {\n\t\treturn new Promise<void>((resolve) => {\n\t\t\tif (pc.iceGatheringState === 'complete') {\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet settled = false;\n\t\t\tconst finish = () => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tpc.removeEventListener('icecandidate', onCandidate);\n\t\t\t\tpc.removeEventListener('icegatheringstatechange', onGatheringComplete);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tconst timeout = setTimeout(finish, ICE_FIRST_RELAY_TIMEOUT_MS);\n\t\t\tconst onCandidate = (e: RTCPeerConnectionIceEvent) => {\n\t\t\t\tif (e.candidate && e.candidate.type === 'relay') finish();\n\t\t\t};\n\t\t\tconst onGatheringComplete = () => {\n\t\t\t\tif (pc.iceGatheringState === 'complete') finish();\n\t\t\t};\n\t\t\tpc.addEventListener('icecandidate', onCandidate);\n\t\t\tpc.addEventListener('icegatheringstatechange', onGatheringComplete);\n\t\t});\n\t}\n\n\tprivate attachRemoteAudio(stream: MediaStream, options: StartCallOptions): void {\n\t\tif (options.audioElement === null) return; // consumer opted out\n\t\tif (!this.remoteAudio) {\n\t\t\tif (options.audioElement) {\n\t\t\t\tthis.remoteAudio = options.audioElement;\n\t\t\t\tthis.ownsAudioElement = false;\n\t\t\t} else {\n\t\t\t\tthis.remoteAudio = new Audio();\n\t\t\t\tthis.remoteAudio.autoplay = true;\n\t\t\t\tthis.ownsAudioElement = true;\n\t\t\t}\n\t\t}\n\t\tthis.remoteAudio.srcObject = stream;\n\t}\n\n\t// -- data-channel protocol ------------------------------------------------\n\n\tprivate handleSignalling(message: SignallingPayload): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'peerLeft':\n\t\t\t\tif (this._state === 'connected' || this._state === 'connecting') {\n\t\t\t\t\tthis.teardown();\n\t\t\t\t\tthis.setState('ended');\n\t\t\t\t\tthis.emitter.emit('ended', { reason: 'peer-left' });\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'renegotiate':\n\t\t\t\tvoid this.renegotiate();\n\t\t\t\tbreak;\n\t\t\tcase 'trackStatus':\n\t\t\t\t// Bot→client trackStatus is not part of the current protocol; ignore.\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Bot asked for a new offer/answer cycle. Re-offer through the same relay\n\t * endpoint. If the backend rejects re-offers (older deployments), the call\n\t * keeps running on the existing session — surfaced via `renegotiated`.\n\t */\n\tprivate async renegotiate(): Promise<void> {\n\t\tif (!this.pc || !this._callId || this.renegotiating) return;\n\t\tthis.renegotiating = true;\n\t\ttry {\n\t\t\tconst offer = await this.pc.createOffer({ iceRestart: true });\n\t\t\tawait this.pc.setLocalDescription(offer);\n\t\t\tawait this.waitForFirstRelayCandidate(this.pc);\n\t\t\tconst answer = await this.signaling.sendOffer(this._callId, {\n\t\t\t\tsdp: this.mustLocalSdp(),\n\t\t\t\ticeServers: this.iceServers,\n\t\t\t});\n\t\t\tawait this.pc.setRemoteDescription({ type: 'answer', sdp: answer.sdp });\n\t\t\tthis.emitter.emit('renegotiated', { ok: true });\n\t\t} catch {\n\t\t\tthis.emitter.emit('renegotiated', { ok: false });\n\t\t} finally {\n\t\t\tthis.renegotiating = false;\n\t\t}\n\t}\n\n\t// -- realtime events (public mode) ----------------------------------------\n\n\t/**\n\t * Public mode only: open the realtime events WebSocket for this call.\n\t * Events double as the pipeline-health signal, and a terminal `status`\n\t * event ends the call from the server side (e.g. max-duration reached).\n\t */\n\tprivate connectEvents(): void {\n\t\tconst session = this.signaling.session;\n\t\tif (!session) return; // authenticated mode: dashboard SSE covers events\n\n\t\tthis.events = new CallEventsClient({\n\t\t\tbaseUrl: this.config.baseUrl,\n\t\t\tgetToken: () => session.getToken(),\n\t\t\tonEvent: (event) => this.handleCallEvent(event),\n\t\t});\n\t\tthis.events.connect();\n\t}\n\n\tprivate handleCallEvent(event: CallEventEnvelope): void {\n\t\t// Events flowing at all = the bot pipeline is alive.\n\t\tthis.confirmPipelineHealthy();\n\t\tthis.emitter.emit('callevent', { event });\n\n\t\tswitch (event.type) {\n\t\t\tcase 'transcript': {\n\t\t\t\tconst role = typeof event.data.role === 'string' ? event.data.role : 'unknown';\n\t\t\t\tconst content = typeof event.data.content === 'string' ? event.data.content : '';\n\t\t\t\tthis.emitter.emit('transcript', { role, content, data: event.data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'interim':\n\t\t\t\tthis.emitter.emit('interim', { data: event.data });\n\t\t\t\tbreak;\n\t\t\tcase 'tts': {\n\t\t\t\tconst text = typeof event.data.text === 'string' ? event.data.text : '';\n\t\t\t\tif (!text) break;\n\t\t\t\tconst timestamp = typeof event.data.timestamp === 'number' ? event.data.timestamp : 0;\n\t\t\t\tthis.emitter.emit('ttschunk', { text, timestamp, data: event.data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'tts_word': {\n\t\t\t\tconst word = typeof event.data.word === 'string' ? event.data.word : '';\n\t\t\t\tif (!word) break;\n\t\t\t\tconst ptsMs = typeof event.data.pts_ms === 'number' ? event.data.pts_ms : null;\n\t\t\t\tconst timestamp = typeof event.data.timestamp === 'number' ? event.data.timestamp : 0;\n\t\t\t\tthis.emitter.emit('ttsword', { word, ptsMs, timestamp, data: event.data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'status': {\n\t\t\t\tconst status = typeof event.data.status === 'string' ? event.data.status : '';\n\t\t\t\tif (TERMINAL_CALL_STATUSES.has(status.toUpperCase() as never)) {\n\t\t\t\t\tthis.endedExternally();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// -- health watchdog ------------------------------------------------------\n\n\tprivate armHealthWatchdog(callId: string): void {\n\t\tthis.pipelineConfirmed = false;\n\t\tthis.healthTimer = setTimeout(() => {\n\t\t\tthis.healthTimer = null;\n\t\t\tif (this.pipelineConfirmed) return;\n\t\t\tvoid this.verifyPipelineHealth(callId);\n\t\t}, PIPELINE_HEALTH_TIMEOUT_MS);\n\t}\n\n\tprivate async verifyPipelineHealth(callId: string): Promise<void> {\n\t\ttry {\n\t\t\tconst call = await this.signaling.getCall(callId);\n\t\t\tif (this.pipelineConfirmed || !this.pc) return;\n\t\t\tif (TERMINAL_CALL_STATUSES.has(call.status)) {\n\t\t\t\tthis.fail(new TalkifCallError('pipeline-dead', `Backend reports terminal status '${call.status}'`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Backend says the call is alive — the silence was the event layer's\n\t\t\t// fault, not the pipeline's.\n\t\t\tthis.pipelineConfirmed = true;\n\t\t} catch {\n\t\t\tif (this.pipelineConfirmed || !this.pc) return;\n\t\t\t// REST unreachable — fall back to the media-plane signal. Flowing\n\t\t\t// audio means alive; later media death is caught by\n\t\t\t// onconnectionstatechange. Kill only when both signals are gone.\n\t\t\tif (this.pc.connectionState === 'connected') {\n\t\t\t\tthis.pipelineConfirmed = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.fail(new TalkifCallError('pipeline-dead', 'No health signal, REST unreachable, media not connected'));\n\t\t}\n\t}\n\n\t// -- internals ------------------------------------------------------------\n\n\tprivate mustLocalSdp(): string {\n\t\tconst sdp = this.pc?.localDescription?.sdp;\n\t\tif (!sdp) {\n\t\t\tthrow new TalkifCallError('signaling-failed', 'Local SDP missing');\n\t\t}\n\t\treturn sdp;\n\t}\n\n\tprivate startDurationTimer(): void {\n\t\tthis._durationSecs = 0;\n\t\tthis.durationTimer = setInterval(() => {\n\t\t\tthis._durationSecs += 1;\n\t\t\tthis.emitter.emit('tick', { durationSecs: this._durationSecs });\n\t\t}, 1_000);\n\t}\n\n\tprivate fail(error: TalkifCallError): void {\n\t\tif (this._state === 'ended' || this._state === 'error') return;\n\t\tthis.teardown();\n\t\tthis.setState('error');\n\t\tthis.emitter.emit('error', { error });\n\t}\n\n\tprivate setState(state: CallState): void {\n\t\tconst previous = this._state;\n\t\tif (previous === state) return;\n\t\tthis._state = state;\n\t\tthis.emitter.emit('statechange', { state, previous });\n\t}\n\n\tprivate teardown(): void {\n\t\tthis.events?.close();\n\t\tthis.events = null;\n\n\t\tif (this.healthTimer) {\n\t\t\tclearTimeout(this.healthTimer);\n\t\t\tthis.healthTimer = null;\n\t\t}\n\t\tthis.pipelineConfirmed = false;\n\n\t\tif (this.durationTimer) {\n\t\t\tclearInterval(this.durationTimer);\n\t\t\tthis.durationTimer = null;\n\t\t}\n\n\t\tthis.dataChannel?.close();\n\t\tthis.dataChannel = null;\n\n\t\tif (this.localStream) {\n\t\t\tfor (const track of this.localStream.getTracks()) track.stop();\n\t\t\tthis.localStream = null;\n\t\t}\n\n\t\tif (this.remoteAudio) {\n\t\t\tthis.remoteAudio.pause();\n\t\t\tthis.remoteAudio.srcObject = null;\n\t\t\tif (this.ownsAudioElement) this.remoteAudio = null;\n\t\t}\n\n\t\tif (this.pc) {\n\t\t\tthis.pc.ontrack = null;\n\t\t\tthis.pc.onconnectionstatechange = null;\n\t\t\tthis.pc.close();\n\t\t\tthis.pc = null;\n\t\t}\n\t}\n}\n\nexport type { CallEndReason };\n"],"mappings":"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","names":[]}
|