{"version":3,"file":"audin-operator-sdk.umd.cjs","sources":["../src/emitter.ts","../src/presence-client.ts","../src/audio-worklet.ts","../src/audio-client.ts","../src/call.ts","../src/token-manager.ts","../src/AudinOperator.ts","../src/codec/mulaw.ts","../src/codec/resample.ts"],"sourcesContent":["/**\n * Tiny dependency-free typed event emitter.\n *\n * Browser `EventTarget` would force `CustomEvent` boxing and loses payload\n * typing; this keeps strong types on `on`/`off`/`emit` with no runtime cost.\n * Listener exceptions are isolated so one bad subscriber cannot break the\n * emit loop or the SDK's internal state machine.\n */\nexport class TypedEmitter<EventMap> {\n  private readonly listeners = new Map<\n    keyof EventMap,\n    Set<(payload: never) => void>\n  >();\n\n  /** Subscribe to `event`. Returns an unsubscribe function. */\n  on<K extends keyof EventMap>(\n    event: K,\n    listener: (payload: EventMap[K]) => void\n  ): () => void {\n    let set = this.listeners.get(event);\n    if (!set) {\n      set = new Set();\n      this.listeners.set(event, set);\n    }\n    set.add(listener as (payload: never) => void);\n    return () => this.off(event, listener);\n  }\n\n  /** Subscribe once: the listener is removed after the first emit. */\n  once<K extends keyof EventMap>(\n    event: K,\n    listener: (payload: EventMap[K]) => void\n  ): () => void {\n    const off = this.on(event, (payload) => {\n      off();\n      listener(payload);\n    });\n    return off;\n  }\n\n  /** Unsubscribe a previously-registered listener. Idempotent. */\n  off<K extends keyof EventMap>(\n    event: K,\n    listener: (payload: EventMap[K]) => void\n  ): void {\n    const set = this.listeners.get(event);\n    if (!set) return;\n    set.delete(listener as (payload: never) => void);\n    if (set.size === 0) this.listeners.delete(event);\n  }\n\n  /** Emit `event` to all current listeners. Listener errors are swallowed. */\n  protected emit<K extends keyof EventMap>(\n    event: K,\n    payload: EventMap[K]\n  ): void {\n    const set = this.listeners.get(event);\n    if (!set) return;\n    // Copy so a listener that unsubscribes during emit doesn't mutate the set\n    // we're iterating.\n    for (const listener of [...set]) {\n      try {\n        (listener as (p: EventMap[K]) => void)(payload);\n      } catch (err) {\n        // Never let a subscriber throw into the SDK's internals.\n        // eslint-disable-next-line no-console\n        console.error(\"[audin-operator-sdk] listener threw:\", err);\n      }\n    }\n  }\n\n  /** Remove every listener (used on teardown). */\n  protected removeAllListeners(): void {\n    this.listeners.clear();\n  }\n}\n","/**\n * PresenceClient — the signalling channel.\n *\n * A single WebSocket to the Audin presence endpoint over which the operator:\n *   - announces availability on a set of phone numbers (`set_available`),\n *   - keeps the connection alive (`ping` heartbeat),\n *   - answers / declines inbound offers (`accept` / `reject`),\n *   - starts outbound calls (`start_outbound`).\n *\n * and over which the server pushes: `connected`, `available_set`,\n * `incoming_call`, `call_taken`, `call_assigned`, `outbound_started`, `pong`,\n * `error`.\n *\n * Resilience built in here so the higher-level `AudinOperator` doesn't have to:\n *   - HEARTBEAT: a `ping` every `heartbeatIntervalMs`; the server reaps idle\n *     connections at ~90s.\n *   - RECONNECT: on unexpected close, walk a backoff schedule, fetch a FRESH\n *     token via `getToken` (the old one may have expired), reconnect, and\n *     re-send the last `set_available` so the operator comes back online\n *     automatically.\n *\n * This class is transport-only: it parses/dispatches messages and owns the\n * socket lifecycle. It holds NO call state and names NO providers.\n */\n\nimport type { OperatorLogger } from \"./types.js\";\n\n/** A server→client message. Loosely typed; the handler narrows on `type`. */\nexport interface PresenceServerMessage {\n  type: string;\n  [key: string]: unknown;\n}\n\nexport interface PresenceClientOptions {\n  /** Base WS URL WITHOUT query, e.g. `wss://host/operator-presence/ws`. */\n  presenceWsUrl: string;\n  /**\n   * Resolve a valid session token (cached & shared with REST/audio). Returns\n   * the raw JWT string. Should re-mint internally once expired.\n   */\n  ensureToken: () => Promise<string>;\n  /**\n   * Drop the cached token so the next {@link ensureToken} re-mints. Invoked on\n   * an auth-related socket close so a reconnect uses a fresh token.\n   */\n  invalidateToken: () => void;\n  heartbeatIntervalMs: number;\n  reconnectBackoffMs: number[];\n  logger: OperatorLogger;\n  /** Invoked for every parsed server message. */\n  onMessage: (msg: PresenceServerMessage) => void;\n  /** Connection lifecycle for the higher layer (state, errors). */\n  onOpen: () => void;\n  onReconnecting: () => void;\n  onError: (code: string, message: string, cause?: unknown) => void;\n}\n\nexport class PresenceClient {\n  private readonly opts: PresenceClientOptions;\n\n  private ws: WebSocket | null = null;\n  private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n  private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n  private reconnectAttempt = 0;\n\n  /** True between `connect()` and `disconnect()` — gates auto-reconnect. */\n  private desiredOnline = false;\n  /** Last availability we announced, replayed after a reconnect. */\n  private lastAvailability: string[] | null = null;\n\n  constructor(opts: PresenceClientOptions) {\n    this.opts = opts;\n  }\n\n  /** Open the presence socket and keep it open (auto-reconnecting). */\n  async connect(): Promise<void> {\n    this.desiredOnline = true;\n    this.reconnectAttempt = 0;\n    await this.openOnce();\n  }\n\n  /**\n   * Close the socket and stop reconnecting. Best-effort `set_unavailable`\n   * first so the server drops presence immediately rather than waiting for the\n   * reaper.\n   */\n  disconnect(): void {\n    this.desiredOnline = false;\n    this.lastAvailability = null;\n    this.clearTimers();\n    if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n      this.sendRaw({ type: \"set_unavailable\" });\n    }\n    if (this.ws) {\n      try {\n        this.ws.close(1000, \"client_offline\");\n      } catch {\n        /* ignore */\n      }\n      this.ws = null;\n    }\n  }\n\n  /** Announce availability on `phoneNumberIds`; remembered for reconnects. */\n  setAvailable(phoneNumberIds: string[]): void {\n    this.lastAvailability = [...phoneNumberIds];\n    this.sendRaw({ type: \"set_available\", phoneNumberIds });\n  }\n\n  /** Drop availability (stays connected). */\n  setUnavailable(): void {\n    this.lastAvailability = null;\n    this.sendRaw({ type: \"set_unavailable\" });\n  }\n\n  /** Answer an inbound offer. */\n  accept(callSid: string): void {\n    this.sendRaw({ type: \"accept\", callSid });\n  }\n\n  /** Decline an inbound offer. */\n  reject(callSid: string): void {\n    this.sendRaw({ type: \"reject\", callSid });\n  }\n\n  /** Request an outbound call. The server replies with `outbound_started`. */\n  startOutbound(to: string, callerId: string): void {\n    this.sendRaw({ type: \"start_outbound\", to, callerId });\n  }\n\n  /** Whether the socket is currently open. */\n  get isOpen(): boolean {\n    return this.ws?.readyState === WebSocket.OPEN;\n  }\n\n  // ──────────────────────────────────────────────────────────────────────\n\n  private async openOnce(): Promise<void> {\n    let token: string;\n    try {\n      token = await this.opts.ensureToken();\n    } catch (err) {\n      const code =\n        (err as { code?: string })?.code ?? \"TOKEN_FETCH_FAILED\";\n      this.opts.onError(code, \"session token unavailable\", err);\n      this.scheduleReconnect();\n      return;\n    }\n\n    const url = `${this.opts.presenceWsUrl}?token=${encodeURIComponent(\n      token\n    )}`;\n    const ws = new WebSocket(url);\n    this.ws = ws;\n\n    ws.onopen = () => {\n      this.opts.logger.debug(\"[presence] WS open\");\n      this.reconnectAttempt = 0;\n      this.startHeartbeat();\n      this.opts.onOpen();\n      // Re-announce availability after a reconnect so we come back online.\n      if (this.lastAvailability && this.lastAvailability.length > 0) {\n        this.sendRaw({\n          type: \"set_available\",\n          phoneNumberIds: this.lastAvailability,\n        });\n      }\n    };\n\n    ws.onmessage = (ev: MessageEvent) => {\n      let parsed: PresenceServerMessage;\n      try {\n        parsed = JSON.parse(\n          typeof ev.data === \"string\" ? ev.data : String(ev.data)\n        );\n      } catch {\n        this.opts.logger.warn(\"[presence] non-JSON message ignored\");\n        return;\n      }\n      this.opts.onMessage(parsed);\n    };\n\n    ws.onerror = (ev) => {\n      this.opts.logger.warn(\"[presence] WS error\", ev);\n      this.opts.onError(\"WS_ERROR\", \"presence socket error\");\n    };\n\n    ws.onclose = (ev: CloseEvent) => {\n      this.opts.logger.debug(\n        `[presence] WS closed code=${ev.code} reason=${ev.reason}`\n      );\n      this.stopHeartbeat();\n      this.ws = null;\n      // An auth-related close means the token was rejected — drop the cache so\n      // the reconnect re-mints rather than replaying the same bad token.\n      if (isAuthCloseCode(ev.code)) {\n        this.opts.invalidateToken();\n      }\n      if (this.desiredOnline) {\n        this.opts.onReconnecting();\n        this.scheduleReconnect();\n      }\n    };\n  }\n\n  private scheduleReconnect(): void {\n    if (!this.desiredOnline) return;\n    if (this.reconnectTimer) return; // one in flight already\n    const schedule = this.opts.reconnectBackoffMs;\n    const idx = Math.min(this.reconnectAttempt, schedule.length - 1);\n    const delay = schedule[idx];\n    this.reconnectAttempt += 1;\n    this.opts.logger.debug(\n      `[presence] reconnect in ${delay}ms (attempt ${this.reconnectAttempt})`\n    );\n    this.reconnectTimer = setTimeout(() => {\n      this.reconnectTimer = null;\n      if (this.desiredOnline) void this.openOnce();\n    }, delay);\n  }\n\n  private startHeartbeat(): void {\n    this.stopHeartbeat();\n    this.heartbeatTimer = setInterval(() => {\n      this.sendRaw({ type: \"ping\" });\n    }, this.opts.heartbeatIntervalMs);\n  }\n\n  private stopHeartbeat(): void {\n    if (this.heartbeatTimer) {\n      clearInterval(this.heartbeatTimer);\n      this.heartbeatTimer = null;\n    }\n  }\n\n  private clearTimers(): void {\n    this.stopHeartbeat();\n    if (this.reconnectTimer) {\n      clearTimeout(this.reconnectTimer);\n      this.reconnectTimer = null;\n    }\n  }\n\n  private sendRaw(message: Record<string, unknown>): void {\n    const ws = this.ws;\n    if (!ws || ws.readyState !== WebSocket.OPEN) {\n      this.opts.logger.warn(\n        `[presence] dropped ${String(message.type)} — socket not open`\n      );\n      return;\n    }\n    try {\n      ws.send(JSON.stringify(message));\n    } catch (err) {\n      this.opts.logger.warn(\"[presence] send failed:\", err);\n    }\n  }\n}\n\n/**\n * Whether a WebSocket close code signals an auth/policy rejection (so the\n * cached token should be discarded before reconnecting). 1008 is the standard\n * \"policy violation\"; 4001/4003 are the conventional app-level auth codes.\n */\nfunction isAuthCloseCode(code: number): boolean {\n  return code === 1008 || code === 4001 || code === 4003;\n}\n","/**\n * AudioWorklet processor source for the operator softphone.\n *\n * IMPORTANT: an `AudioWorkletProcessor` runs in `AudioWorkletGlobalScope`, a\n * separate realm with NO module system and NO access to the SDK's other\n * modules. So everything it needs — μ-law codec and linear resampling — is\n * inlined here as a single self-contained source STRING. At runtime the SDK\n * turns this string into a `Blob` URL and `audioWorklet.addModule()`s it (see\n * `audio-client.ts`). Keeping it as an exported string (rather than a separate\n * `.js` asset) means the published bundle is a single file with no asset paths\n * to resolve — the right shape for an embeddable SDK.\n *\n * Two responsibilities, one processor instance:\n *\n *   - CAPTURE: `process()` receives 128-frame Float32 blocks at the context\n *     sample rate (typically 48 kHz). It buffers them, resamples down to\n *     8 kHz, μ-law-encodes, and `postMessage`s the resulting bytes to the main\n *     thread, which forwards them on the audio WebSocket.\n *   - PLAYBACK: the main thread `postMessage`s μ-law bytes received from the\n *     WebSocket; the processor decodes them, upsamples 8 kHz → context rate,\n *     and queues PCM that `process()` drains into the output. A small jitter\n *     buffer smooths network arrival; underflow emits silence.\n *\n * The μ-law tables/logic here are byte-for-byte the same algorithm as\n * `src/codec/mulaw.ts` (kept in sync; the codec KAT guards the canonical\n * source). The inline copy exists only because the worklet realm can't import.\n */\n\n/**\n * The processor source as a string, ready to be wrapped in a Blob URL.\n * `__PROCESSOR_NAME__` is replaced by {@link OPERATOR_WORKLET_NAME} below so\n * the registered name and the name the main thread references can never drift.\n */\nconst PROCESSOR_SOURCE = String.raw`\n// ---- G.711 μ-law (mirrors src/codec/mulaw.ts; standard Sun convention) ----\nvar MULAW_BIAS = 0x84;\nvar MULAW_CLIP = 32635;\nvar MULAW_EXP_LUT = new Int8Array([\n  0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,\n  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,\n  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7\n]);\nfunction encodeMuLawSample(sample) {\n  var pcm = sample;\n  if (pcm > 32767) pcm = 32767; else if (pcm < -32768) pcm = -32768;\n  var sign = (pcm >> 8) & 0x80;\n  if (sign !== 0) pcm = -pcm;\n  if (pcm > MULAW_CLIP) pcm = MULAW_CLIP;\n  pcm += MULAW_BIAS;\n  var exponent = MULAW_EXP_LUT[(pcm >> 7) & 0xff];\n  var mantissa = (pcm >> (exponent + 3)) & 0x0f;\n  return (~(sign | (exponent << 4) | mantissa)) & 0xff;\n}\nfunction decodeMuLawSample(byte) {\n  var u = ~byte & 0xff;\n  var sign = u & 0x80;\n  var exponent = (u >> 4) & 0x07;\n  var mantissa = u & 0x0f;\n  var sample = ((mantissa << 3) + MULAW_BIAS) << exponent;\n  sample -= MULAW_BIAS;\n  return (sign !== 0 ? -sample : sample) || 0;\n}\n\nvar TARGET_RATE = 8000;\n// Cap the playback jitter buffer so a stall can't grow memory unbounded\n// (~2s of 8kHz audio decoded to context rate).\nvar MAX_PLAYBACK_SAMPLES = 96000;\n\nclass OperatorAudioProcessor extends AudioWorkletProcessor {\n  constructor() {\n    super();\n    // Capture accumulator (context-rate Float32) and its fractional read head\n    // for resampling to 8kHz across process() boundaries.\n    this._capBuf = new Float32Array(0);\n    this._capPos = 0; // fractional position into _capBuf for the next 8k sample\n\n    // Playback queue (context-rate Float32 already upsampled) + read head.\n    this._playBuf = new Float32Array(0);\n    this._playPos = 0;\n\n    this._muted = false;\n\n    this.port.onmessage = (e) => {\n      var d = e.data;\n      if (!d) return;\n      if (d.type === \"mulaw\") {\n        this._enqueuePlayback(d.payload);\n      } else if (d.type === \"mute\") {\n        this._muted = !!d.on;\n      } else if (d.type === \"flush\") {\n        this._playBuf = new Float32Array(0);\n        this._playPos = 0;\n      }\n    };\n  }\n\n  // Decode μ-law (8kHz) -> upsample to context rate -> append to playback queue.\n  _enqueuePlayback(mulawBytes) {\n    var n = mulawBytes.length;\n    if (n === 0) return;\n    var ctxRate = sampleRate; // global in AudioWorkletGlobalScope\n    var ratio = ctxRate / TARGET_RATE; // e.g. 6 for 48k\n    var outLen = Math.round(n * ratio);\n    var upsampled = new Float32Array(outLen);\n    var last = n - 1;\n    for (var i = 0; i < outLen; i++) {\n      var srcPos = i / ratio;\n      var i0 = Math.floor(srcPos);\n      var frac = srcPos - i0;\n      var a = decodeMuLawSample(mulawBytes[i0 > last ? last : i0]) / 32768;\n      var bIdx = i0 + 1 > last ? last : i0 + 1;\n      var b = decodeMuLawSample(mulawBytes[bIdx]) / 32768;\n      upsampled[i] = a + (b - a) * frac;\n    }\n\n    // Compact any already-consumed head, then append, bounded by the cap.\n    var remaining = this._playBuf.length - this._playPos;\n    var combinedLen = remaining + upsampled.length;\n    if (combinedLen > MAX_PLAYBACK_SAMPLES) {\n      // Drop oldest audio to stay bounded (better latency than OOM on a stall).\n      var keep = MAX_PLAYBACK_SAMPLES - upsampled.length;\n      if (keep < 0) keep = 0;\n      var trimmed = new Float32Array(keep + upsampled.length);\n      if (keep > 0) {\n        trimmed.set(\n          this._playBuf.subarray(this._playBuf.length - keep, this._playBuf.length),\n          0\n        );\n      }\n      trimmed.set(upsampled, keep);\n      this._playBuf = trimmed;\n      this._playPos = 0;\n      return;\n    }\n    var merged = new Float32Array(combinedLen);\n    merged.set(this._playBuf.subarray(this._playPos), 0);\n    merged.set(upsampled, remaining);\n    this._playBuf = merged;\n    this._playPos = 0;\n  }\n\n  // CAPTURE: append context-rate input, then emit as many 8k μ-law samples as\n  // the accumulated buffer allows. Keeps a fractional read head so resampling\n  // is continuous across blocks.\n  _captureAndEmit(input) {\n    var prevLen = this._capBuf.length;\n    var headStart = Math.floor(this._capPos);\n    // The resample read head can overshoot past the end of the previous buffer\n    // (the loop below advances pos by up to one full step beyond the last\n    // produced sample). Clamp how much of the old buffer we drop so grown.set never gets\n    // a negative offset (which would throw and freeze the worklet after block 1).\n    var dropped = headStart < prevLen ? headStart : prevLen;\n    var tailLen = prevLen - dropped; // >= 0\n    var grown = new Float32Array(tailLen + input.length);\n    if (tailLen > 0) grown.set(this._capBuf.subarray(dropped), 0);\n    grown.set(input, tailLen);\n    this._capBuf = grown;\n    this._capPos -= dropped; // read head relative to the new buffer start\n\n    var ctxRate = sampleRate;\n    var step = ctxRate / TARGET_RATE; // input samples per 8k output sample\n    var last = this._capBuf.length - 1;\n    var out = [];\n    var pos = this._capPos;\n    while (pos + 1 <= last) {\n      var i0 = Math.floor(pos);\n      var frac = pos - i0;\n      var a = this._capBuf[i0];\n      var b = this._capBuf[i0 + 1];\n      var s = a + (b - a) * frac; // float [-1,1]\n      var pcm = s < 0 ? s * 0x8000 : s * 0x7fff;\n      out.push(encodeMuLawSample(pcm | 0));\n      pos += step;\n    }\n    this._capPos = pos;\n\n    if (out.length > 0 && !this._muted) {\n      var bytes = new Uint8Array(out);\n      // Transfer the buffer to avoid a copy on the way to the main thread.\n      this.port.postMessage({ type: \"mulaw\", payload: bytes }, [bytes.buffer]);\n    }\n  }\n\n  process(inputs, outputs) {\n    // --- capture ---\n    var input = inputs[0];\n    if (input && input.length > 0 && input[0] && input[0].length > 0) {\n      this._captureAndEmit(input[0]);\n    }\n\n    // --- playback ---\n    var output = outputs[0];\n    if (output && output.length > 0) {\n      var chan0 = output[0];\n      var frames = chan0.length;\n      var avail = this._playBuf.length - this._playPos;\n      var toCopy = avail < frames ? avail : frames;\n      for (var i = 0; i < toCopy; i++) {\n        var v = this._playBuf[this._playPos + i];\n        for (var c = 0; c < output.length; c++) output[c][i] = v;\n      }\n      for (var j = toCopy; j < frames; j++) {\n        for (var c2 = 0; c2 < output.length; c2++) output[c2][j] = 0;\n      }\n      this._playPos += toCopy;\n      // Reclaim memory once the buffer is fully drained.\n      if (this._playPos >= this._playBuf.length) {\n        this._playBuf = new Float32Array(0);\n        this._playPos = 0;\n      }\n    }\n\n    return true; // keep processor alive\n  }\n}\n\nregisterProcessor(\"__PROCESSOR_NAME__\", OperatorAudioProcessor);\n`;\n\n/** Registered name of the worklet processor. */\nexport const OPERATOR_WORKLET_NAME = \"audin-operator-audio-processor\";\n\n/** The processor source with its registered name substituted in. */\nexport const OPERATOR_WORKLET_SOURCE = PROCESSOR_SOURCE.replace(\n  \"__PROCESSOR_NAME__\",\n  OPERATOR_WORKLET_NAME\n);\n\n/**\n * Build a Blob URL for the worklet module. The caller is responsible for\n * `URL.revokeObjectURL` after `addModule` resolves. Lives here (not in\n * audio-client) so the source string and its packaging stay together.\n */\nexport function createWorkletBlobUrl(): string {\n  const blob = new Blob([OPERATOR_WORKLET_SOURCE], {\n    type: \"application/javascript\",\n  });\n  return URL.createObjectURL(blob);\n}\n","/**\n * AudioBridge — the audio leg of a single call.\n *\n * Joins three things for one `callSid`:\n *   1. the microphone (`getUserMedia`),\n *   2. an `AudioWorklet` that does the μ-law 8 kHz transcoding + resampling\n *      (see `audio-worklet.ts`), and\n *   3. the audio WebSocket to the Audin service.\n *\n * Data flow:\n *   mic → AudioContext source → worklet → (μ-law bytes) → WS binary frame\n *   WS binary frame → (μ-law bytes) → worklet → AudioContext destination → 🔊\n *\n * Control (mute / DTMF / hangup) is sent as JSON text frames on the same WS.\n *\n * The bridge owns the lifecycle of everything it creates and tears it ALL\n * down on `close()` (mic tracks stopped, worklet disconnected, context closed,\n * socket closed) so a call leak can't keep the microphone hot.\n *\n * No provider names appear anywhere here — it is just \"audio over a WebSocket\".\n */\n\nimport {\n  OPERATOR_WORKLET_NAME,\n  createWorkletBlobUrl,\n} from \"./audio-worklet.js\";\nimport type { OperatorLogger } from \"./types.js\";\n\n/** Reason strings the bridge reports to its `onClosed` callback. */\nexport type BridgeCloseReason =\n  | \"local_hangup\"\n  | \"remote_close\"\n  | \"ws_error\"\n  | \"mic_error\"\n  | \"teardown\";\n\nexport interface AudioBridgeOptions {\n  /** `wss://…/operator-audio/ws/:callSid?token=…` — already fully formed. */\n  audioWsUrl: string;\n  /** Microphone constraints for `getUserMedia`. */\n  audioConstraints: MediaTrackConstraints;\n  logger: OperatorLogger;\n  /** Called once when the bridge finishes tearing down (any reason). */\n  onClosed: (reason: BridgeCloseReason) => void;\n}\n\n/**\n * Narrow surface of the global APIs the bridge needs. Declared so the file\n * type-checks under a DOM lib without us reaching for `any`.\n */\ntype GetUserMedia = (constraints: {\n  audio: MediaTrackConstraints | boolean;\n}) => Promise<MediaStream>;\n\nexport class AudioBridge {\n  private readonly opts: AudioBridgeOptions;\n\n  private ws: WebSocket | null = null;\n  private audioContext: AudioContext | null = null;\n  private micStream: MediaStream | null = null;\n  private sourceNode: MediaStreamAudioSourceNode | null = null;\n  private workletNode: AudioWorkletNode | null = null;\n  private blobUrl: string | null = null;\n\n  private closed = false;\n  private muted = false;\n\n  constructor(opts: AudioBridgeOptions) {\n    this.opts = opts;\n  }\n\n  /**\n   * Open the WS, capture the mic, load the worklet and wire the graph. Resolves\n   * once audio is flowing both ways (or throws on a fatal setup error, having\n   * already torn down whatever was partially created).\n   */\n  async start(): Promise<void> {\n    try {\n      await this.openSocket();\n      await this.setupAudioGraph();\n    } catch (err) {\n      this.opts.logger.error(\"[audio] start failed:\", err);\n      this.close(\"mic_error\");\n      throw err;\n    }\n  }\n\n  /** Mute / unmute the operator microphone toward the far end. */\n  setMuted(on: boolean): void {\n    this.muted = on;\n    // Gate inside the worklet (drops capture frames) …\n    this.workletNode?.port.postMessage({ type: \"mute\", on });\n    // … and tell the server so the relay also gates (belt and suspenders).\n    this.sendControl({ type: \"mute\", on });\n  }\n\n  get isMuted(): boolean {\n    return this.muted;\n  }\n\n  /**\n   * Send a DTMF digit as a control message.\n   *\n   * Note: not yet honored end-to-end — the server does not currently forward\n   * the tones onto the telephone network, so this is a functional no-op for\n   * the far end today (the control message itself stays valid for the future).\n   */\n  sendDtmf(digit: string): void {\n    this.sendControl({ type: \"dtmf\", digit });\n  }\n\n  /** Tell the server to hang up, then tear down locally. */\n  hangup(): void {\n    this.sendControl({ type: \"hangup\" });\n    this.close(\"local_hangup\");\n  }\n\n  /**\n   * Tear down everything this bridge created. Idempotent. Fires `onClosed`\n   * exactly once with the FIRST reason it was closed with.\n   */\n  close(reason: BridgeCloseReason): void {\n    if (this.closed) return;\n    this.closed = true;\n\n    // Stop the mic first so the capture indicator clears immediately.\n    if (this.micStream) {\n      for (const track of this.micStream.getTracks()) {\n        try {\n          track.stop();\n        } catch {\n          /* ignore */\n        }\n      }\n      this.micStream = null;\n    }\n\n    try {\n      this.workletNode?.port.postMessage({ type: \"flush\" });\n    } catch {\n      /* ignore */\n    }\n    try {\n      this.workletNode?.disconnect();\n    } catch {\n      /* ignore */\n    }\n    try {\n      this.sourceNode?.disconnect();\n    } catch {\n      /* ignore */\n    }\n    this.workletNode = null;\n    this.sourceNode = null;\n\n    if (this.audioContext) {\n      this.audioContext.close().catch(() => {\n        /* ignore */\n      });\n      this.audioContext = null;\n    }\n\n    if (this.ws) {\n      try {\n        // 1000 = normal closure.\n        this.ws.close(1000, reason);\n      } catch {\n        /* ignore */\n      }\n      this.ws = null;\n    }\n\n    if (this.blobUrl) {\n      URL.revokeObjectURL(this.blobUrl);\n      this.blobUrl = null;\n    }\n\n    this.opts.onClosed(reason);\n  }\n\n  // ──────────────────────────────────────────────────────────────────────\n\n  private openSocket(): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n      let settled = false;\n      const ws = new WebSocket(this.opts.audioWsUrl);\n      // Binary frames arrive as ArrayBuffer (avoids a Blob round-trip).\n      ws.binaryType = \"arraybuffer\";\n      this.ws = ws;\n\n      ws.onopen = () => {\n        settled = true;\n        this.opts.logger.debug(\"[audio] WS open\");\n        resolve();\n      };\n\n      ws.onmessage = (ev: MessageEvent) => this.onWsMessage(ev);\n\n      ws.onerror = (ev) => {\n        this.opts.logger.warn(\"[audio] WS error\", ev);\n        if (!settled) {\n          settled = true;\n          reject(new Error(\"audio WebSocket failed to open\"));\n        }\n      };\n\n      ws.onclose = (ev: CloseEvent) => {\n        this.opts.logger.debug(\n          `[audio] WS closed code=${ev.code} reason=${ev.reason}`\n        );\n        // A server-initiated close (or any close after open) tears the bridge\n        // down as a remote close, unless we're already closing locally.\n        if (!this.closed) {\n          this.close(\"remote_close\");\n        }\n      };\n    });\n  }\n\n  private onWsMessage(ev: MessageEvent): void {\n    const data = ev.data;\n    // BINARY → μ-law audio from the far end → feed the worklet for playback.\n    if (data instanceof ArrayBuffer) {\n      const bytes = new Uint8Array(data);\n      // Copy into a transferable buffer for the worklet message.\n      const copy = bytes.slice();\n      this.workletNode?.port.postMessage({ type: \"mulaw\", payload: copy }, [\n        copy.buffer,\n      ]);\n      return;\n    }\n    // TEXT → JSON control/notification from the server (e.g. error). We don't\n    // act on much here; the presence channel drives lifecycle. Log for debug.\n    if (typeof data === \"string\") {\n      this.opts.logger.debug(\"[audio] control:\", data);\n    }\n  }\n\n  private async setupAudioGraph(): Promise<void> {\n    const Ctx: typeof AudioContext =\n      window.AudioContext ||\n      (window as unknown as { webkitAudioContext: typeof AudioContext })\n        .webkitAudioContext;\n    if (!Ctx) {\n      throw new Error(\"Web Audio API not available in this environment\");\n    }\n    const audioContext = new Ctx();\n    this.audioContext = audioContext;\n    // Autoplay policies can leave the context suspended until a user gesture.\n    if (audioContext.state === \"suspended\") {\n      await audioContext.resume().catch(() => {\n        /* a gesture may be required; playback resumes when it happens */\n      });\n    }\n\n    // Load the worklet module from a Blob URL (single-file bundle friendly).\n    this.blobUrl = createWorkletBlobUrl();\n    await audioContext.audioWorklet.addModule(this.blobUrl);\n\n    // Capture the microphone.\n    const getUserMedia: GetUserMedia | undefined =\n      navigator.mediaDevices?.getUserMedia?.bind(navigator.mediaDevices);\n    if (!getUserMedia) {\n      throw new Error(\"getUserMedia not available in this environment\");\n    }\n    let micStream: MediaStream;\n    try {\n      micStream = await getUserMedia({ audio: this.opts.audioConstraints });\n    } catch (err) {\n      const e = new Error(\"microphone permission denied or unavailable\");\n      (e as { code?: string }).code = \"MIC_PERMISSION_DENIED\";\n      (e as { cause?: unknown }).cause = err;\n      throw e;\n    }\n    this.micStream = micStream;\n\n    // Wire: mic source → worklet → destination.\n    const sourceNode = audioContext.createMediaStreamSource(micStream);\n    const workletNode = new AudioWorkletNode(\n      audioContext,\n      OPERATOR_WORKLET_NAME,\n      {\n        numberOfInputs: 1,\n        numberOfOutputs: 1,\n        outputChannelCount: [1],\n      }\n    );\n    this.sourceNode = sourceNode;\n    this.workletNode = workletNode;\n\n    // Surface any uncaught error thrown inside the worklet's process() so it is\n    // not silently swallowed (without a handler the processor stops with no log).\n    workletNode.onprocessorerror = (e) =>\n      this.opts.logger.error(\"[audio] worklet processor error\", e);\n\n    // μ-law bytes produced by the worklet (from the mic) → send on the WS.\n    workletNode.port.onmessage = (e: MessageEvent) => {\n      const msg = e.data as { type?: string; payload?: Uint8Array };\n      if (msg?.type === \"mulaw\" && msg.payload) {\n        this.sendAudio(msg.payload);\n      }\n    };\n\n    sourceNode.connect(workletNode);\n    workletNode.connect(audioContext.destination);\n\n    // Apply any mute that was requested before the graph existed.\n    if (this.muted) {\n      workletNode.port.postMessage({ type: \"mute\", on: true });\n    }\n  }\n\n  private sendAudio(bytes: Uint8Array): void {\n    const ws = this.ws;\n    if (!ws || ws.readyState !== WebSocket.OPEN) return;\n    try {\n      // Send the raw μ-law bytes as a single binary frame.\n      ws.send(bytes);\n    } catch (err) {\n      this.opts.logger.warn(\"[audio] send audio failed:\", err);\n    }\n  }\n\n  private sendControl(message: Record<string, unknown>): void {\n    const ws = this.ws;\n    if (!ws || ws.readyState !== WebSocket.OPEN) return;\n    try {\n      ws.send(JSON.stringify(message));\n    } catch (err) {\n      this.opts.logger.warn(\"[audio] send control failed:\", err);\n    }\n  }\n}\n","/**\n * CallHandle — the concrete `OperatorCall` returned to the application.\n *\n * It is a thin, stateful facade: lifecycle transitions and the actual\n * accept/reject/dial/teardown side-effects live in `AudinOperator`, which\n * injects them here as callbacks. This keeps the handle dumb (no socket / no\n * audio knowledge) while giving the app a stable object whose getters reflect\n * the live state the operator mutates.\n */\n\nimport type {\n  OperatorCall,\n  CallDirection,\n  CallState,\n  CallEndReason,\n} from \"./types.js\";\n\n/** Side-effect callbacks injected by {@link AudinOperator}. */\nexport interface CallHandleDeps {\n  onAccept: (call: CallHandle) => void;\n  onReject: (call: CallHandle) => void;\n  onMute: (call: CallHandle, on: boolean) => void;\n  onDtmf: (call: CallHandle, digit: string) => void;\n  onHangup: (call: CallHandle) => void;\n}\n\nexport interface CallHandleInit {\n  callSid: string;\n  direction: CallDirection;\n  from?: string;\n  to?: string;\n  /** Initial state — `ringing` for inbound offers, `connecting` for outbound. */\n  state: CallState;\n}\n\nconst DTMF_RE = /^[0-9*#]$/;\n\nexport class CallHandle implements OperatorCall {\n  readonly callSid: string;\n  readonly direction: CallDirection;\n  readonly from?: string;\n  readonly to?: string;\n\n  private _state: CallState;\n  private _endReason?: CallEndReason;\n  private _muted = false;\n  private readonly deps: CallHandleDeps;\n\n  constructor(init: CallHandleInit, deps: CallHandleDeps) {\n    this.callSid = init.callSid;\n    this.direction = init.direction;\n    this.from = init.from;\n    this.to = init.to;\n    this._state = init.state;\n    this.deps = deps;\n  }\n\n  get state(): CallState {\n    return this._state;\n  }\n\n  get endReason(): CallEndReason | undefined {\n    return this._endReason;\n  }\n\n  get muted(): boolean {\n    return this._muted;\n  }\n\n  // ── public API ──────────────────────────────────────────────────────────\n\n  accept(): void {\n    if (this._state !== \"ringing\") return;\n    this._state = \"connecting\";\n    this.deps.onAccept(this);\n  }\n\n  reject(): void {\n    if (this._state !== \"ringing\") return;\n    // Mark ended BEFORE the dep so that when AudinOperator emits `callEnded`\n    // from onReject, this handle already reflects state=\"ended\" / reason.\n    this.markEnded(\"rejected\");\n    this.deps.onReject(this);\n  }\n\n  mute(on: boolean): void {\n    if (this._state === \"ended\") return;\n    this._muted = on;\n    this.deps.onMute(this, on);\n  }\n\n  sendDtmf(digit: string): void {\n    if (this._state === \"ended\") return;\n    if (!DTMF_RE.test(digit)) {\n      throw new Error(`invalid DTMF digit: ${JSON.stringify(digit)}`);\n    }\n    this.deps.onDtmf(this, digit);\n  }\n\n  hangup(): void {\n    if (this._state === \"ended\") return;\n    // Mark ended BEFORE the dep so the handle reflects the terminal state by\n    // the time AudinOperator emits `callEnded` (idempotent if the bridge\n    // teardown also marks it).\n    this.markEnded(\"hangup\");\n    this.deps.onHangup(this);\n  }\n\n  // ── internal state transitions (called by AudinOperator) ─────────────────\n\n  /** Move to `connecting` (audio bridge opening). */\n  setConnecting(): void {\n    if (this._state === \"ended\") return;\n    this._state = \"connecting\";\n  }\n\n  /** Move to `active` (audio flowing). */\n  setActive(): void {\n    if (this._state === \"ended\") return;\n    this._state = \"active\";\n  }\n\n  /** Terminal transition. Idempotent — keeps the first end reason. */\n  markEnded(reason: CallEndReason): void {\n    if (this._state === \"ended\") return;\n    this._state = \"ended\";\n    this._endReason = reason;\n  }\n}\n","/**\n * TokenManager — centralises session-token acquisition for the SDK.\n *\n * Both transports (presence WS, audio WS) and the REST helpers\n * ({@link AudinOperator.listPhoneNumbers}) need the SAME short-lived session\n * token. Rather than each call site hitting {@link GetTokenFn} independently,\n * they go through {@link TokenManager.ensureToken}, which:\n *   - returns a cached token while it is still valid, or\n *   - fetches a fresh one via `getToken`, caches it, and returns it.\n *\n * Validity is decided from the token's optional `expiresAt` (ISO-8601),\n * applying a safety skew so we never present a token that's about to lapse\n * mid-request. When no `expiresAt` is supplied, we fall back to a conservative\n * TTL so a token is still reused across the closely-spaced calls of a single\n * connect (e.g. listPhoneNumbers → goOnline) without being held forever.\n *\n * On an auth failure (e.g. a 401 from REST, or an auth-related WS close), the\n * caller invokes {@link TokenManager.invalidate} so the next `ensureToken`\n * re-mints. Concurrent `ensureToken` calls share a single in-flight fetch.\n */\n\nimport type { GetTokenFn, OperatorSessionToken } from \"./types.js\";\n\n/** Refresh this many ms BEFORE `expiresAt` to avoid mid-request expiry. */\nconst EXPIRY_SKEW_MS = 30_000;\n/** TTL assumed when the token carries no `expiresAt`. Conservative on purpose. */\nconst DEFAULT_TTL_MS = 60_000;\n\ninterface CachedToken {\n  token: string;\n  /** Epoch ms after which we must re-mint. */\n  expiresAtMs: number;\n}\n\nexport class TokenManager {\n  private readonly getToken: GetTokenFn;\n  private cached: CachedToken | null = null;\n  private inFlight: Promise<string> | null = null;\n\n  constructor(getToken: GetTokenFn) {\n    this.getToken = getToken;\n  }\n\n  /**\n   * Resolve a valid session token, reusing the cached one when possible.\n   * Concurrent callers share a single underlying `getToken()` invocation.\n   */\n  async ensureToken(): Promise<string> {\n    const now = Date.now();\n    if (this.cached && this.cached.expiresAtMs > now) {\n      return this.cached.token;\n    }\n    if (this.inFlight) {\n      return this.inFlight;\n    }\n\n    this.inFlight = this.fetchAndCache().finally(() => {\n      this.inFlight = null;\n    });\n    return this.inFlight;\n  }\n\n  /**\n   * Drop the cached token so the next {@link ensureToken} re-mints. Call this\n   * on an auth failure (401 / auth-related WS close).\n   */\n  invalidate(): void {\n    this.cached = null;\n  }\n\n  private async fetchAndCache(): Promise<string> {\n    const session = await this.getToken();\n    if (!session?.token) {\n      throw new TokenError(\n        \"TOKEN_INVALID\",\n        \"getToken() returned no token\"\n      );\n    }\n    this.cached = {\n      token: session.token,\n      expiresAtMs: computeExpiry(session, Date.now()),\n    };\n    return session.token;\n  }\n}\n\n/** Tagged error so callers can distinguish token problems from transport ones. */\nexport class TokenError extends Error {\n  readonly code: string;\n  constructor(code: string, message: string) {\n    super(message);\n    this.name = \"TokenError\";\n    this.code = code;\n  }\n}\n\nfunction computeExpiry(session: OperatorSessionToken, nowMs: number): number {\n  if (session.expiresAt) {\n    const parsed = Date.parse(session.expiresAt);\n    if (!Number.isNaN(parsed)) {\n      // Never trust a token right up to its stated expiry.\n      return Math.max(parsed - EXPIRY_SKEW_MS, nowMs);\n    }\n  }\n  return nowMs + DEFAULT_TTL_MS;\n}\n","/**\n * AudinOperator — the public entry point of `@audin.ai/operator-sdk`.\n *\n * A headless (no-UI) controller an operator's browser app drives to make and\n * receive phone calls through Audin. It hides:\n *   - the signalling channel (PresenceClient: availability, ringing, dialing),\n *   - the media channel (AudioBridge: microphone, μ-law transcoding, playback),\n *   - reconnection, heartbeats and token refresh.\n *\n * Credentials never enter the browser: the app supplies a `getToken` callback\n * that calls ITS OWN backend (which holds the account key) to mint a\n * short-lived session token. The SDK only ever sees that token.\n *\n * MVP concurrency: ONE active call at a time. While a call is live, an inbound\n * offer is auto-declined (`reject`) and `dial()` rejects with an error. This\n * keeps the audio graph and state machine simple; multi-line can layer on top\n * later without changing the public surface.\n *\n * Naming is provider-neutral throughout — this is \"the Audin operator\n * softphone\", nothing more.\n */\n\nimport { TypedEmitter } from \"./emitter.js\";\nimport { PresenceClient } from \"./presence-client.js\";\nimport type { PresenceServerMessage } from \"./presence-client.js\";\nimport { AudioBridge } from \"./audio-client.js\";\nimport type { BridgeCloseReason } from \"./audio-client.js\";\nimport { CallHandle } from \"./call.js\";\nimport { TokenManager } from \"./token-manager.js\";\nimport type {\n  AudinOperatorConfig,\n  AudinOperatorEventMap,\n  AudinOperatorEventName,\n  AudinOperatorListener,\n  DialOptions,\n  OperatorCall,\n  OperatorLogger,\n  OperatorPhoneNumber,\n  PresenceState,\n} from \"./types.js\";\n\nconst DEFAULT_HEARTBEAT_MS = 25_000;\nconst DEFAULT_BACKOFF_MS = [1000, 2000, 5000, 10_000, 30_000];\nconst DEFAULT_AUDIO_CONSTRAINTS: MediaTrackConstraints = {\n  echoCancellation: true,\n  noiseSuppression: true,\n  autoGainControl: true,\n};\n/** How long to wait for `outbound_started` after `start_outbound`. */\nconst OUTBOUND_ACK_TIMEOUT_MS = 15_000;\n\nexport class AudinOperator extends TypedEmitter<AudinOperatorEventMap> {\n  private readonly presence: PresenceClient;\n  private readonly coreWsHost: string;\n  private readonly coreHttpBase: string;\n  private readonly tokens: TokenManager;\n  /**\n   * Microphone constraints handed to the next {@link AudioBridge} when a call's\n   * audio leg opens. Mutable so {@link setAudioConstraints} can switch the input\n   * device at runtime; read fresh in {@link openAudioBridge} (never captured\n   * earlier), so the change applies from the next call.\n   */\n  private audioConstraints: MediaTrackConstraints;\n  private readonly logger: OperatorLogger;\n\n  private presenceState: PresenceState = \"offline\";\n\n  /** The single active/ringing call (MVP: one at a time). */\n  private activeCall: CallHandle | null = null;\n  private activeBridge: AudioBridge | null = null;\n\n  /** Pending outbound dial awaiting its `outbound_started` ack. */\n  private pendingOutbound: {\n    to: string;\n    callerId: string;\n    resolve: (call: OperatorCall) => void;\n    reject: (err: Error) => void;\n    timer: ReturnType<typeof setTimeout>;\n  } | null = null;\n\n  constructor(config: AudinOperatorConfig) {\n    super();\n    this.tokens = new TokenManager(config.getToken);\n    this.logger = config.logger ?? console;\n    this.audioConstraints =\n      config.audioConstraints ?? DEFAULT_AUDIO_CONSTRAINTS;\n    this.coreWsHost = toWsBase(config.coreUrl);\n    this.coreHttpBase = toHttpBase(config.coreUrl);\n\n    this.presence = new PresenceClient({\n      presenceWsUrl: `${this.coreWsHost}/operator-presence/ws`,\n      ensureToken: () => this.tokens.ensureToken(),\n      invalidateToken: () => this.tokens.invalidate(),\n      heartbeatIntervalMs:\n        config.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS,\n      reconnectBackoffMs: config.reconnectBackoffMs ?? DEFAULT_BACKOFF_MS,\n      logger: this.logger,\n      onMessage: (msg) => this.handlePresenceMessage(msg),\n      onOpen: () => this.setPresenceState(\"online\"),\n      onReconnecting: () => this.setPresenceState(\"reconnecting\"),\n      onError: (code, message, cause) =>\n        this.emitError(code, message, cause),\n    });\n  }\n\n  // ── typed event surface (re-export emitter methods publicly) ─────────────\n\n  on<K extends AudinOperatorEventName>(\n    event: K,\n    listener: AudinOperatorListener<K>\n  ): () => void {\n    return super.on(event, listener);\n  }\n\n  off<K extends AudinOperatorEventName>(\n    event: K,\n    listener: AudinOperatorListener<K>\n  ): void {\n    super.off(event, listener);\n  }\n\n  once<K extends AudinOperatorEventName>(\n    event: K,\n    listener: AudinOperatorListener<K>\n  ): () => void {\n    return super.once(event, listener);\n  }\n\n  // ── numbers ────────────────────────────────────────────────────────────\n\n  /**\n   * List the phone numbers this account owns and may go online on / dial from.\n   *\n   * Fetches over REST using the SAME short-lived session token the WebSockets\n   * use (obtained through `getToken`) — the Account API Key never enters the\n   * browser. On a 401 the cached token is invalidated and the request is\n   * retried ONCE with a fresh token; a second 401 throws an\n   * {@link OperatorRequestError} with code `UNAUTHORIZED`. Other non-OK\n   * responses throw with code `REQUEST_FAILED`.\n   *\n   * Use a returned number's `id` for {@link goOnline} and its `phoneNumber`\n   * (E.164) as the `callerId` for {@link dial}.\n   */\n  async listPhoneNumbers(): Promise<OperatorPhoneNumber[]> {\n    return this.fetchPhoneNumbers(true);\n  }\n\n  private async fetchPhoneNumbers(\n    retryOn401: boolean\n  ): Promise<OperatorPhoneNumber[]> {\n    const token = await this.tokens.ensureToken();\n    const url = `${this.coreHttpBase}/operator/phone-numbers`;\n\n    let res: Response;\n    try {\n      res = await fetch(url, {\n        headers: { Authorization: `Bearer ${token}` },\n      });\n    } catch (err) {\n      throw new OperatorRequestError(\n        \"REQUEST_FAILED\",\n        \"failed to reach the operator service\",\n        err\n      );\n    }\n\n    if (res.status === 401) {\n      // Token rejected — drop it and (once) retry with a fresh mint.\n      this.tokens.invalidate();\n      if (retryOn401) {\n        return this.fetchPhoneNumbers(false);\n      }\n      throw new OperatorRequestError(\n        \"UNAUTHORIZED\",\n        \"session token rejected fetching phone numbers (401)\"\n      );\n    }\n\n    if (!res.ok) {\n      throw new OperatorRequestError(\n        \"REQUEST_FAILED\",\n        `phone-numbers request failed with status ${res.status}`\n      );\n    }\n\n    let body: { phoneNumbers?: unknown };\n    try {\n      body = await res.json();\n    } catch (err) {\n      throw new OperatorRequestError(\n        \"REQUEST_FAILED\",\n        \"phone-numbers response was not valid JSON\",\n        err\n      );\n    }\n\n    return Array.isArray(body.phoneNumbers)\n      ? (body.phoneNumbers as OperatorPhoneNumber[])\n      : [];\n  }\n\n  // ── lifecycle ────────────────────────────────────────────────────────────\n\n  /**\n   * Go online and start accepting inbound calls on `phoneNumberIds`. Connects\n   * the presence channel (fetching a token via `getToken`) and announces\n   * availability. Safe to call again to change the number set.\n   */\n  async goOnline(phoneNumberIds: string[]): Promise<void> {\n    if (!Array.isArray(phoneNumberIds) || phoneNumberIds.length === 0) {\n      throw new Error(\"goOnline requires a non-empty phoneNumberIds array\");\n    }\n    this.setPresenceState(\"connecting\");\n    if (!this.presence.isOpen) {\n      await this.presence.connect();\n    }\n    this.presence.setAvailable(phoneNumberIds);\n  }\n\n  /**\n   * Go offline: drop availability, tear down any active call, and close the\n   * presence channel (no auto-reconnect until `goOnline` again).\n   */\n  async goOffline(): Promise<void> {\n    if (this.activeCall) {\n      this.teardownActiveCall(\"offline\");\n    }\n    this.presence.disconnect();\n    this.setPresenceState(\"offline\");\n  }\n\n  /** Current presence channel state. */\n  get state(): PresenceState {\n    return this.presenceState;\n  }\n\n  /** The current active/ringing call, if any. */\n  get currentCall(): OperatorCall | null {\n    return this.activeCall;\n  }\n\n  /**\n   * Switch the microphone constraints used to capture the operator's audio —\n   * typically to pick a specific input device, e.g.\n   * `setAudioConstraints({ deviceId: { exact: id }, echoCancellation: true })`.\n   *\n   * Takes effect from the NEXT call: a call already in progress keeps the\n   * device its {@link AudioBridge} opened with (this does not re-open the\n   * microphone mid-call). The new constraints are read when the next\n   * `dial`/accepted-inbound opens its audio leg.\n   */\n  setAudioConstraints(constraints: MediaTrackConstraints): void {\n    this.audioConstraints = constraints;\n  }\n\n  // ── outbound ─────────────────────────────────────────────────────────────\n\n  /**\n   * Place an outbound call to `to` (E.164), presenting `options.callerId`\n   * (which must be an active number your account owns). Resolves once the call\n   * is accepted by the platform and the audio bridge is opening; rejects on\n   * validation failure, a busy operator, or if the platform doesn't ack in\n   * time.\n   */\n  dial(to: string, options: DialOptions): Promise<OperatorCall> {\n    if (!this.presence.isOpen) {\n      return Promise.reject(\n        new Error(\"not online — call goOnline() before dialing\")\n      );\n    }\n    if (this.activeCall) {\n      return Promise.reject(\n        new Error(\"an active call is already in progress (MVP: one at a time)\")\n      );\n    }\n    if (this.pendingOutbound) {\n      return Promise.reject(new Error(\"an outbound dial is already pending\"));\n    }\n    if (!to || typeof to !== \"string\") {\n      return Promise.reject(new Error(\"dial requires a destination number\"));\n    }\n    if (!options?.callerId) {\n      return Promise.reject(new Error(\"dial requires options.callerId\"));\n    }\n\n    return new Promise<OperatorCall>((resolve, reject) => {\n      const timer = setTimeout(() => {\n        this.pendingOutbound = null;\n        reject(new Error(\"outbound call was not acknowledged in time\"));\n      }, OUTBOUND_ACK_TIMEOUT_MS);\n\n      this.pendingOutbound = {\n        to,\n        callerId: options.callerId,\n        resolve,\n        reject,\n        timer,\n      };\n      this.presence.startOutbound(to, options.callerId);\n    });\n  }\n\n  // ── presence message handling ────────────────────────────────────────────\n\n  private handlePresenceMessage(msg: PresenceServerMessage): void {\n    switch (msg.type) {\n      case \"connected\":\n        // Acknowledged by onOpen → presence \"online\". Nothing else to do.\n        break;\n\n      case \"pong\":\n        break;\n\n      case \"available_set\": {\n        const accepted = asStringArray(msg.accepted);\n        const rejected = asStringArray(msg.rejected);\n        this.emitEvent(\"availabilityChanged\", { accepted, rejected });\n        break;\n      }\n\n      case \"unavailable_set\":\n        break;\n\n      case \"incoming_call\":\n        this.handleIncomingCall(msg);\n        break;\n\n      case \"call_taken\":\n        this.handleCallTaken(msg);\n        break;\n\n      case \"call_assigned\":\n        this.handleCallAssigned(msg);\n        break;\n\n      case \"outbound_started\":\n        this.handleOutboundStarted(msg);\n        break;\n\n      case \"call_ended\":\n        this.handleCallEnded(msg);\n        break;\n\n      case \"error\": {\n        const code = typeof msg.code === \"string\" ? msg.code : \"SERVER_ERROR\";\n        const message =\n          typeof msg.message === \"string\" ? msg.message : \"server error\";\n        this.emitError(code, message, msg);\n        // If an outbound dial was in flight, an error aborts it.\n        this.failPendingOutbound(new Error(`${code}: ${message}`));\n        break;\n      }\n\n      default:\n        this.logger.debug(\"[operator] unhandled presence message:\", msg.type);\n    }\n  }\n\n  private handleIncomingCall(msg: PresenceServerMessage): void {\n    const callSid = typeof msg.callSid === \"string\" ? msg.callSid : \"\";\n    if (!callSid) return;\n    const from = typeof msg.from === \"string\" ? msg.from : undefined;\n    const to = typeof msg.to === \"string\" ? msg.to : undefined;\n\n    // MVP: one call at a time. If busy, auto-decline so we don't get assigned a\n    // call we can't service, and don't bother the app.\n    if (this.activeCall) {\n      this.logger.debug(\n        `[operator] auto-rejecting incoming ${callSid} — already on a call`\n      );\n      this.presence.reject(callSid);\n      return;\n    }\n\n    const call = this.makeCall({\n      callSid,\n      direction: \"inbound\",\n      from,\n      to,\n      state: \"ringing\",\n    });\n    this.activeCall = call;\n    this.emitEvent(\"incomingCall\", call);\n  }\n\n  private handleCallTaken(msg: PresenceServerMessage): void {\n    const callSid = typeof msg.callSid === \"string\" ? msg.callSid : \"\";\n    const call = this.activeCall;\n    if (!call || call.callSid !== callSid) return;\n    // Another operator won this offer (or we lost the race). End it.\n    call.markEnded(\"taken_by_other\");\n    this.activeCall = null;\n    this.emitEvent(\"callEnded\", call);\n  }\n\n  private handleCallAssigned(msg: PresenceServerMessage): void {\n    const callSid = typeof msg.callSid === \"string\" ? msg.callSid : \"\";\n    const call = this.activeCall;\n    if (!call || call.callSid !== callSid) {\n      this.logger.warn(\n        `[operator] call_assigned for unknown callSid=${callSid} — ignoring`\n      );\n      return;\n    }\n    // We won the inbound offer → open the audio bridge.\n    call.setConnecting();\n    void this.openAudioBridge(call);\n  }\n\n  private handleOutboundStarted(msg: PresenceServerMessage): void {\n    const callSid = typeof msg.callSid === \"string\" ? msg.callSid : \"\";\n    const pending = this.pendingOutbound;\n    if (!pending) {\n      this.logger.warn(\n        `[operator] outbound_started without a pending dial (callSid=${callSid})`\n      );\n      return;\n    }\n    clearTimeout(pending.timer);\n    this.pendingOutbound = null;\n\n    if (!callSid) {\n      pending.reject(new Error(\"outbound_started missing callSid\"));\n      return;\n    }\n\n    const call = this.makeCall({\n      callSid,\n      direction: \"outbound\",\n      to: pending.to,\n      state: \"connecting\",\n    });\n    this.activeCall = call;\n    pending.resolve(call);\n    void this.openAudioBridge(call);\n  }\n\n  /**\n   * Explicit end-of-call notification from the platform (presence channel).\n   *\n   * Authoritative: the platform sends `call_ended` when the call terminates\n   * for ANY reason — remote hangup, no answer, busy, failure — including the\n   * cases where the audio leg never opened at all (e.g. an outbound the far\n   * end never answered), so the audio-WS close alone could never signal them.\n   */\n  private handleCallEnded(msg: PresenceServerMessage): void {\n    const callSid = typeof msg.callSid === \"string\" ? msg.callSid : \"\";\n    const call = this.activeCall;\n    if (!call || call.callSid !== callSid) return;\n\n    const serverReason = typeof msg.reason === \"string\" ? msg.reason : \"\";\n    const endReason: \"no_answer\" | \"failed\" | \"remote_hangup\" =\n      serverReason.includes(\"no-answer\")\n        ? \"no_answer\"\n        : serverReason.includes(\"busy\") ||\n            serverReason.includes(\"failed\") ||\n            serverReason.includes(\"canceled\")\n          ? \"failed\"\n          : \"remote_hangup\";\n\n    // Clear the refs BEFORE closing the bridge: its synchronous onClosed →\n    // onBridgeClosed sees activeCall === null and short-circuits, so the app\n    // receives exactly ONE callEnded for this call.\n    const bridge = this.activeBridge;\n    this.activeCall = null;\n    this.activeBridge = null;\n    call.markEnded(endReason);\n    bridge?.close(\"teardown\");\n    this.emitEvent(\"callEnded\", call);\n  }\n\n  // ── audio bridge wiring ──────────────────────────────────────────────────\n\n  private async openAudioBridge(call: CallHandle): Promise<void> {\n    let token: string;\n    try {\n      // Reuse the shared (cached) session token; the manager re-mints if it's\n      // expired, so the audio leg never opens with a stale token.\n      token = await this.tokens.ensureToken();\n    } catch (err) {\n      const code =\n        (err as { code?: string })?.code ?? \"TOKEN_FETCH_FAILED\";\n      this.emitError(code, \"session token unavailable\", err);\n      this.endCallWithReason(call, \"failed\");\n      return;\n    }\n\n    const audioWsUrl = `${this.coreWsHost}/operator-audio/ws/${encodeURIComponent(\n      call.callSid\n    )}?token=${encodeURIComponent(token)}`;\n\n    const bridge = new AudioBridge({\n      audioWsUrl,\n      audioConstraints: this.audioConstraints,\n      logger: this.logger,\n      onClosed: (reason) => this.onBridgeClosed(call, reason),\n    });\n    this.activeBridge = bridge;\n\n    try {\n      await bridge.start();\n    } catch (err) {\n      const code =\n        (err as { code?: string })?.code ?? \"AUDIO_START_FAILED\";\n      this.emitError(code, (err as Error).message ?? \"audio start failed\", err);\n      this.endCallWithReason(call, \"failed\");\n      return;\n    }\n\n    // Apply mute set before the bridge existed.\n    if (call.muted) bridge.setMuted(true);\n\n    call.setActive();\n    this.emitEvent(\"callStarted\", call);\n  }\n\n  private onBridgeClosed(call: CallHandle, reason: BridgeCloseReason): void {\n    // Only react if this is still the active call's bridge.\n    if (this.activeCall !== call) return;\n    this.activeBridge = null;\n    const endReason =\n      reason === \"local_hangup\"\n        ? \"hangup\"\n        : reason === \"ws_error\" || reason === \"mic_error\"\n          ? \"failed\"\n          : \"remote_hangup\";\n    call.markEnded(endReason);\n    this.activeCall = null;\n    this.emitEvent(\"callEnded\", call);\n  }\n\n  // ── CallHandle side-effect deps ──────────────────────────────────────────\n\n  private makeCall(init: {\n    callSid: string;\n    direction: \"inbound\" | \"outbound\";\n    from?: string;\n    to?: string;\n    state: \"ringing\" | \"connecting\";\n  }): CallHandle {\n    return new CallHandle(init, {\n      onAccept: (c) => {\n        // Tell the server we take this offer. The audio bridge opens when the\n        // server confirms with `call_assigned`.\n        this.presence.accept(c.callSid);\n      },\n      onReject: (c) => {\n        this.presence.reject(c.callSid);\n        if (this.activeCall === c) {\n          this.activeCall = null;\n          // CallHandle.reject() will markEnded(\"rejected\"); emit so the app\n          // gets a consistent terminal event for the declined offer.\n          this.emitEvent(\"callEnded\", c);\n        }\n      },\n      onMute: (_c, on) => {\n        this.activeBridge?.setMuted(on);\n      },\n      onDtmf: (_c, digit) => {\n        this.activeBridge?.sendDtmf(digit);\n      },\n      onHangup: (c) => {\n        if (this.activeBridge) {\n          // The bridge's onClosed → onBridgeClosed emits `callEnded` and clears\n          // activeCall/activeBridge synchronously. Don't duplicate that here.\n          this.activeBridge.hangup();\n          return;\n        }\n        // No bridge yet (e.g. ringing/connecting) — drop locally, tell the\n        // server via reject so it doesn't keep ringing us, and emit callEnded\n        // ourselves since there's no bridge teardown to do it. (CallHandle has\n        // already marked the call ended with reason \"hangup\".)\n        this.presence.reject(c.callSid);\n        if (this.activeCall === c) {\n          this.activeCall = null;\n          this.activeBridge = null;\n          this.emitEvent(\"callEnded\", c);\n        }\n      },\n    });\n  }\n\n  // ── helpers ──────────────────────────────────────────────────────────────\n\n  private endCallWithReason(\n    call: CallHandle,\n    reason: \"failed\" | \"remote_hangup\"\n  ): void {\n    // Clear the refs BEFORE closing the bridge: its synchronous onClosed →\n    // onBridgeClosed then short-circuits on the activeCall guard, so the app\n    // receives exactly ONE callEnded (and with THIS reason, not the bridge's).\n    const bridge = this.activeBridge;\n    this.activeBridge = null;\n    if (this.activeCall === call) this.activeCall = null;\n    call.markEnded(reason);\n    bridge?.close(\"teardown\");\n    this.emitEvent(\"callEnded\", call);\n  }\n\n  private teardownActiveCall(\n    reason: \"offline\" | \"failed\" | \"remote_hangup\"\n  ): void {\n    const call = this.activeCall;\n    if (!call) return;\n    // Same null-first ordering as endCallWithReason (single callEnded emit).\n    const bridge = this.activeBridge;\n    this.activeBridge = null;\n    this.activeCall = null;\n    call.markEnded(reason);\n    bridge?.close(\"teardown\");\n    this.emitEvent(\"callEnded\", call);\n  }\n\n  private failPendingOutbound(err: Error): void {\n    const pending = this.pendingOutbound;\n    if (!pending) return;\n    clearTimeout(pending.timer);\n    this.pendingOutbound = null;\n    pending.reject(err);\n  }\n\n  private setPresenceState(state: PresenceState): void {\n    if (this.presenceState === state) return;\n    this.presenceState = state;\n    this.emitEvent(\"presenceStateChanged\", state);\n  }\n\n  private emitError(code: string, message: string, cause?: unknown): void {\n    this.emitEvent(\"error\", { code, message, cause });\n  }\n\n  /** Public-facing emit wrapper (the base `emit` is protected). */\n  private emitEvent<K extends AudinOperatorEventName>(\n    event: K,\n    payload: AudinOperatorEventMap[K]\n  ): void {\n    // `emit` is protected on the base; this subclass method exposes it\n    // internally so the handlers above stay readable.\n    (this as unknown as {\n      emit: (e: K, p: AudinOperatorEventMap[K]) => void;\n    }).emit(event, payload);\n  }\n}\n\n// ── module-private utilities ───────────────────────────────────────────────\n\n/**\n * Normalise a base URL into a `ws(s)://host` origin with no trailing slash and\n * no path. Accepts `http(s)` or `ws(s)`; upgrades the scheme accordingly.\n */\nexport function toWsBase(coreUrl: string): string {\n  let u: URL;\n  try {\n    u = new URL(coreUrl);\n  } catch {\n    throw new Error(`invalid coreUrl: ${JSON.stringify(coreUrl)}`);\n  }\n  const scheme =\n    u.protocol === \"https:\" || u.protocol === \"wss:\" ? \"wss:\" : \"ws:\";\n  return `${scheme}//${u.host}`;\n}\n\n/**\n * Normalise a base URL into an `http(s)://host` origin with no trailing slash\n * and no path. Accepts `http(s)` or `ws(s)`; downgrades the WS scheme to the\n * matching HTTP scheme for REST calls.\n */\nexport function toHttpBase(coreUrl: string): string {\n  let u: URL;\n  try {\n    u = new URL(coreUrl);\n  } catch {\n    throw new Error(`invalid coreUrl: ${JSON.stringify(coreUrl)}`);\n  }\n  const scheme =\n    u.protocol === \"https:\" || u.protocol === \"wss:\" ? \"https:\" : \"http:\";\n  return `${scheme}//${u.host}`;\n}\n\n/** Error thrown by the SDK's REST helpers (e.g. {@link AudinOperator.listPhoneNumbers}). */\nexport class OperatorRequestError extends Error {\n  /** Stable machine code: `UNAUTHORIZED` | `REQUEST_FAILED`. */\n  readonly code: string;\n  /** Original error / response context, if any. */\n  readonly cause?: unknown;\n  constructor(code: string, message: string, cause?: unknown) {\n    super(message);\n    this.name = \"OperatorRequestError\";\n    this.code = code;\n    this.cause = cause;\n  }\n}\n\nfunction asStringArray(v: unknown): string[] {\n  return Array.isArray(v)\n    ? v.filter((x): x is string => typeof x === \"string\")\n    : [];\n}\n","/**\n * G.711 μ-law (mu-law) codec — pure, dependency-free, browser-safe.\n *\n * This is the ONLY wire-format-critical piece of the SDK: the bytes produced\n * by {@link encodeMuLaw} are streamed verbatim over the audio WebSocket, and\n * the platform expects them to be byte-compatible with the ITU-T G.711 μ-law\n * standard at 8 kHz. A Known-Answer-Test (`__tests__/mulaw.test.ts`) pins the\n * encoder output for canonical PCM inputs so any regression in this module\n * fails loudly before it can corrupt a live call.\n *\n * Reference: ITU-T Recommendation G.711, μ-law encoding.\n *\n * This is the canonical Sun Microsystems `g711.c` `linear2ulaw` / `ulaw2linear`\n * implementation, the universally-cited C reference for the standard. In this\n * convention the sign bit is SET for NEGATIVE samples, which yields the\n * standard telephony anchors the Audin audio channel expects:\n *\n *   - digital silence (PCM 0)     → 0xFF\n *   - full-scale positive (+32767) → 0x80\n *   - full-scale negative (-32768) → 0x00\n *\n * Algorithm (BIAS = 0x84, segment/exponent table):\n *   - Input is signed 16-bit linear PCM (Int16, -32768..32767).\n *   - sign = (sample >> 8) & 0x80 (set for negative); take |sample|.\n *   - Clamp magnitude to 32635 (CLIP), add BIAS (132).\n *   - Find the exponent (segment) = position of the highest set bit above the\n *     bias floor; mantissa = next 4 bits.\n *   - μ-law byte = ~(sign | (exponent << 4) | mantissa)  (one's complement).\n *\n * Decoding is the exact inverse, reconstructing the quantised linear value.\n *\n * NOTE: μ-law has two codes (0x7F and 0xFF) that both decode to 0, so\n * `encode(decode(b))` is NOT the identity for every one of the 256 codes —\n * but `decode(encode(x))` is a faithful quantisation of `x`. The KAT pins the\n * encoder, which is the only direction that touches the wire.\n *\n * No allocation per-sample beyond the output typed array; suitable for the\n * audio hot path.\n */\n\n/** Additive bias applied before segment search (G.711: 0x84 = 132). */\nconst BIAS = 0x84;\n/** Magnitude clip — the largest linear value μ-law can represent. */\nconst CLIP = 32635;\n\n/**\n * Per-value exponent lookup for the 8-bit magnitude window used during the\n * segment search. `expLut[i]` is the μ-law segment (0..7) for a magnitude\n * whose top byte (after biasing, shifted right by 7) equals `i`. Standard\n * Sun/G.711 table.\n */\nconst EXP_LUT = new Int8Array([\n  0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n  4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,\n  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n]);\n\n/**\n * Encode a single signed 16-bit PCM sample to one μ-law byte (0..255).\n *\n * Exposed for tests / single-sample use; {@link encodeMuLaw} is the bulk path.\n */\nexport function encodeMuLawSample(sample: number): number {\n  // Clamp to Int16 range defensively (callers may pass float-rounded values).\n  let pcm = sample;\n  if (pcm > 32767) pcm = 32767;\n  else if (pcm < -32768) pcm = -32768;\n\n  // Sign bit: SET (0x80) for NEGATIVE samples — the canonical Sun/G.711\n  // convention. `(pcm >> 8) & 0x80` extracts the sign of the 16-bit value.\n  const sign = (pcm >> 8) & 0x80;\n  if (sign !== 0) pcm = -pcm;\n\n  if (pcm > CLIP) pcm = CLIP;\n  pcm += BIAS;\n\n  const exponent = EXP_LUT[(pcm >> 7) & 0xff];\n  const mantissa = (pcm >> (exponent + 3)) & 0x0f;\n  const muLawByte = ~(sign | (exponent << 4) | mantissa) & 0xff;\n  return muLawByte;\n}\n\n/**\n * Decode a single μ-law byte (0..255) back to a signed 16-bit PCM sample.\n *\n * Exposed for tests / single-sample use; {@link decodeMuLaw} is the bulk path.\n */\nexport function decodeMuLawSample(muLawByte: number): number {\n  const u = ~muLawByte & 0xff;\n  const sign = u & 0x80;\n  const exponent = (u >> 4) & 0x07;\n  const mantissa = u & 0x0f;\n\n  // Reconstruct the quantised magnitude, then remove the bias.\n  let sample = ((mantissa << 3) + BIAS) << exponent;\n  sample -= BIAS;\n\n  // Sign bit is SET for NEGATIVE samples (see encoder), so a SET sign bit\n  // means a negative reconstruction. `|| 0` normalises the `-0` that the two\n  // zero-codes (0x7F, 0xFF) would otherwise produce — both decode to +0.\n  return (sign !== 0 ? -sample : sample) || 0;\n}\n\n/**\n * Encode a buffer of signed 16-bit PCM samples to μ-law bytes.\n *\n * One output byte per input sample. Returns a fresh `Uint8Array`.\n */\nexport function encodeMuLaw(pcm: Int16Array): Uint8Array {\n  const out = new Uint8Array(pcm.length);\n  for (let i = 0; i < pcm.length; i++) {\n    out[i] = encodeMuLawSample(pcm[i]);\n  }\n  return out;\n}\n\n/**\n * Decode a buffer of μ-law bytes to signed 16-bit PCM samples.\n *\n * One output sample per input byte. Returns a fresh `Int16Array`.\n */\nexport function decodeMuLaw(muLaw: Uint8Array): Int16Array {\n  const out = new Int16Array(muLaw.length);\n  for (let i = 0; i < muLaw.length; i++) {\n    out[i] = decodeMuLawSample(muLaw[i]);\n  }\n  return out;\n}\n","/**\n * Linear sample-rate conversion — pure, dependency-free, browser-safe.\n *\n * The audio WebSocket carries 8 kHz μ-law, but a browser `AudioContext`\n * typically runs at 48 kHz (sometimes 44.1 kHz). These helpers bridge the gap:\n *\n *   - capture path:  downsample(captureRate → 8000) then μ-law encode\n *   - playback path: μ-law decode then upsample(8000 → playbackRate)\n *\n * Linear interpolation is intentionally simple. It is NOT a brick-wall\n * anti-alias filter — for an 8 kHz voice band downsampled from 48 kHz the\n * aliasing is mild and speech-intelligible, which is the right trade-off for a\n * dependency-free SDK hot path. (The `AudioWorklet` additionally benefits from\n * the browser's own resampling at the graph boundary.)\n *\n * Both functions are referentially transparent (no shared state, no\n * allocation beyond the returned array), so they are unit-testable without a\n * browser and safe to call per audio frame.\n */\n\n/**\n * Resample a block of mono Float32 PCM samples from `fromRate` to `toRate`\n * using linear interpolation.\n *\n * The output length is `round(input.length * toRate / fromRate)`. Edge samples\n * are clamped (no out-of-range reads). When `fromRate === toRate` the input is\n * returned as a copy.\n */\nexport function resampleLinear(\n  input: Float32Array,\n  fromRate: number,\n  toRate: number\n): Float32Array {\n  if (fromRate <= 0 || toRate <= 0) {\n    throw new Error(\"resampleLinear: sample rates must be positive\");\n  }\n  if (fromRate === toRate || input.length === 0) {\n    return input.slice();\n  }\n\n  const ratio = fromRate / toRate;\n  const outLength = Math.round(input.length / ratio);\n  const out = new Float32Array(outLength);\n  const lastIndex = input.length - 1;\n\n  for (let i = 0; i < outLength; i++) {\n    // Position in the input timeline for output sample i.\n    const srcPos = i * ratio;\n    const i0 = Math.floor(srcPos);\n    const frac = srcPos - i0;\n    const a = input[Math.min(i0, lastIndex)];\n    const b = input[Math.min(i0 + 1, lastIndex)];\n    out[i] = a + (b - a) * frac;\n  }\n\n  return out;\n}\n\n/**\n * Downsample to 8 kHz. Thin wrapper around {@link resampleLinear} for the\n * capture path (any context rate → 8000).\n */\nexport function downsampleTo8k(\n  input: Float32Array,\n  fromRate: number\n): Float32Array {\n  return resampleLinear(input, fromRate, 8000);\n}\n\n/**\n * Upsample from 8 kHz. Thin wrapper around {@link resampleLinear} for the\n * playback path (8000 → any context rate).\n */\nexport function upsampleFrom8k(\n  input: Float32Array,\n  toRate: number\n): Float32Array {\n  return resampleLinear(input, 8000, toRate);\n}\n\n/**\n * Convert Float32 PCM in [-1, 1] to signed 16-bit PCM. Values outside the\n * range are clamped. Pure; returns a fresh `Int16Array`.\n */\nexport function floatToInt16(input: Float32Array): Int16Array {\n  const out = new Int16Array(input.length);\n  for (let i = 0; i < input.length; i++) {\n    let s = input[i];\n    if (s > 1) s = 1;\n    else if (s < -1) s = -1;\n    // Asymmetric full-scale: positive max is 32767, negative min is -32768.\n    out[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n  }\n  return out;\n}\n\n/**\n * Convert signed 16-bit PCM to Float32 in [-1, 1]. Pure; returns a fresh\n * `Float32Array`.\n */\nexport function int16ToFloat(input: Int16Array): Float32Array {\n  const out = new Float32Array(input.length);\n  for (let i = 0; i < input.length; i++) {\n    const s = input[i];\n    out[i] = s < 0 ? s / 0x8000 : s / 0x7fff;\n  }\n  return out;\n}\n"],"names":["TypedEmitter","event","listener","set","off","payload","err","PresenceClient","opts","phoneNumberIds","callSid","to","callerId","token","code","url","ws","ev","parsed","isAuthCloseCode","schedule","idx","delay","message","PROCESSOR_SOURCE","OPERATOR_WORKLET_NAME","OPERATOR_WORKLET_SOURCE","createWorkletBlobUrl","blob","AudioBridge","on","digit","reason","track","resolve","reject","settled","data","copy","Ctx","audioContext","getUserMedia","micStream","e","sourceNode","workletNode","msg","bytes","DTMF_RE","CallHandle","init","deps","EXPIRY_SKEW_MS","DEFAULT_TTL_MS","TokenManager","getToken","now","session","TokenError","computeExpiry","nowMs","DEFAULT_HEARTBEAT_MS","DEFAULT_BACKOFF_MS","DEFAULT_AUDIO_CONSTRAINTS","OUTBOUND_ACK_TIMEOUT_MS","AudinOperator","config","toWsBase","toHttpBase","cause","retryOn401","res","OperatorRequestError","body","constraints","options","timer","accepted","asStringArray","rejected","from","call","pending","serverReason","endReason","bridge","audioWsUrl","c","_c","state","coreUrl","u","v","x","BIAS","CLIP","EXP_LUT","encodeMuLawSample","sample","pcm","sign","exponent","mantissa","decodeMuLawSample","muLawByte","encodeMuLaw","out","i","decodeMuLaw","muLaw","resampleLinear","input","fromRate","toRate","ratio","outLength","lastIndex","srcPos","i0","frac","a","b","downsampleTo8k","upsampleFrom8k","floatToInt16","int16ToFloat"],"mappings":"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AG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAC3E,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,CACnC,CAAC,EAOM,SAASC,EAAkBC,EAAwB,CAExD,IAAIC,EAAMD,EACNC,EAAM,MAAOA,EAAM,MACdA,EAAM,SAAQA,EAAM,QAI7B,MAAMC,EAAQD,GAAO,EAAK,IACtBC,IAAS,IAAGD,EAAM,CAACA,GAEnBA,EAAMJ,IAAMI,EAAMJ,GACtBI,GAAOL,EAEP,MAAMO,EAAWL,EAASG,GAAO,EAAK,GAAI,EACpCG,EAAYH,GAAQE,EAAW,EAAM,GAE3C,MADkB,EAAED,EAAQC,GAAY,EAAKC,GAAY,GAE3D,CAOO,SAASC,EAAkBC,EAA2B,CAC3D,MAAMb,EAAI,CAACa,EAAY,IACjBJ,EAAOT,EAAI,IACXU,EAAYV,GAAK,EAAK,EAI5B,IAAIO,IAHaP,EAAI,KAGM,GAAKG,GAASO,EACzC,OAAAH,GAAUJ,GAKFM,IAAS,EAAI,CAACF,EAASA,IAAW,CAC5C,CAOO,SAASO,EAAYN,EAA6B,CACvD,MAAMO,EAAM,IAAI,WAAWP,EAAI,MAAM,EACrC,QAASQ,EAAI,EAAGA,EAAIR,EAAI,OAAQQ,IAC9BD,EAAIC,CAAC,EAAIV,EAAkBE,EAAIQ,CAAC,CAAC,EAEnC,OAAOD,CACT,CAOO,SAASE,EAAYC,EAA+B,CACzD,MAAMH,EAAM,IAAI,WAAWG,EAAM,MAAM,EACvC,QAASF,EAAI,EAAGA,EAAIE,EAAM,OAAQF,IAChCD,EAAIC,CAAC,EAAIJ,EAAkBM,EAAMF,CAAC,CAAC,EAErC,OAAOD,CACT,CC3GO,SAASI,EACdC,EACAC,EACAC,EACc,CACd,GAAID,GAAY,GAAKC,GAAU,EAC7B,MAAM,IAAI,MAAM,+CAA+C,EAEjE,GAAID,IAAaC,GAAUF,EAAM,SAAW,EAC1C,OAAOA,EAAM,MAAA,EAGf,MAAMG,EAAQF,EAAWC,EACnBE,EAAY,KAAK,MAAMJ,EAAM,OAASG,CAAK,EAC3CR,EAAM,IAAI,aAAaS,CAAS,EAChCC,EAAYL,EAAM,OAAS,EAEjC,QAASJ,EAAI,EAAGA,EAAIQ,EAAWR,IAAK,CAElC,MAAMU,EAASV,EAAIO,EACbI,EAAK,KAAK,MAAMD,CAAM,EACtBE,EAAOF,EAASC,EAChBE,EAAIT,EAAM,KAAK,IAAIO,EAAIF,CAAS,CAAC,EACjCK,EAAIV,EAAM,KAAK,IAAIO,EAAK,EAAGF,CAAS,CAAC,EAC3CV,EAAIC,CAAC,EAAIa,GAAKC,EAAID,GAAKD,CACzB,CAEA,OAAOb,CACT,CAMO,SAASgB,EACdX,EACAC,EACc,CACd,OAAOF,EAAeC,EAAOC,EAAU,GAAI,CAC7C,CAMO,SAASW,EACdZ,EACAE,EACc,CACd,OAAOH,EAAeC,EAAO,IAAME,CAAM,CAC3C,CAMO,SAASW,EAAab,EAAiC,CAC5D,MAAML,EAAM,IAAI,WAAWK,EAAM,MAAM,EACvC,QAASJ,EAAI,EAAGA,EAAII,EAAM,OAAQJ,IAAK,CACrC,IAAI,EAAII,EAAMJ,CAAC,EACX,EAAI,EAAG,EAAI,EACN,EAAI,KAAI,EAAI,IAErBD,EAAIC,CAAC,EAAI,EAAI,EAAI,EAAI,MAAS,EAAI,KACpC,CACA,OAAOD,CACT,CAMO,SAASmB,EAAad,EAAiC,CAC5D,MAAML,EAAM,IAAI,aAAaK,EAAM,MAAM,EACzC,QAASJ,EAAI,EAAGA,EAAII,EAAM,OAAQJ,IAAK,CACrC,MAAM,EAAII,EAAMJ,CAAC,EACjBD,EAAIC,CAAC,EAAI,EAAI,EAAI,EAAI,MAAS,EAAI,KACpC,CACA,OAAOD,CACT"}