{"version":3,"sources":["../src/errors.ts","../src/transport/livekit.ts","../src/index.ts","../src/audio/output.ts","../src/audio/analysis.ts","../src/session/agentSession.ts","../src/events.ts","../src/sessionToken.ts","../src/session/emitter.ts","../src/session/toolDispatcher.ts","../src/session/wakeLock.ts"],"sourcesContent":["export type FishAgentErrorCode =\n  | \"session_request_failed\"\n  | \"agent_not_public\"\n  | \"origin_forbidden\"\n  | \"unsupported_transport\"\n  | \"mic_permission_denied\"\n  /** A requested audio device could not be activated, or the browser does not support selecting it. */\n  | \"device_change_failed\"\n  | \"connection_failed\"\n  | \"session_expired\"\n  | \"tool_failed\"\n  | \"tool_timeout\"\n  /** An upstream model/speech provider failed during the session. */\n  | \"provider_error\"\n  /** The agent runtime hit an internal error. */\n  | \"internal_error\";\n\nexport class FishAgentError extends Error {\n  readonly code: FishAgentErrorCode;\n  /** HTTP status of the failing session request, when the error originated there. */\n  readonly statusCode?: number;\n\n  constructor(\n    code: FishAgentErrorCode,\n    message: string,\n    options?: { statusCode?: number; cause?: unknown },\n  ) {\n    super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n    this.name = \"FishAgentError\";\n    this.code = code;\n    this.statusCode = options?.statusCode;\n  }\n}\n","import {\n  ConnectionState,\n  createLocalAudioTrack,\n  DisconnectReason,\n  Room,\n  RoomEvent,\n  Track,\n  TrackEvent,\n  type LocalAudioTrack,\n  type RemoteTrack,\n} from \"livekit-client\";\nimport {\n  AGENT_EVENT_TOPIC,\n  CLIENT_EVENT_TOPIC,\n  type AgentSessionMessage,\n  type ClientSessionMessage,\n  type SessionToken,\n} from \"@fishaudio/agent-protocol\";\nimport { FishAgentError } from \"../errors.js\";\nimport type { Transport, TransportCallbacks, TransportConnectOptions } from \"./types.js\";\n\nfunction hasMessageType(value: unknown): value is { type: string } {\n  return (\n    typeof value === \"object\" &&\n    value !== null &&\n    typeof (value as { type?: unknown }).type === \"string\"\n  );\n}\n\nfunction captureMicrophone(inputDeviceId?: string): Promise<LocalAudioTrack> {\n  return createLocalAudioTrack({\n    deviceId: inputDeviceId,\n    echoCancellation: true,\n    noiseSuppression: true,\n    autoGainControl: true,\n  });\n}\n\n/** Sole production Transport. LiveKit vocabulary must not escape this file,\n * except the `Room` handed out through the `getRoom()` escape hatch. */\nexport class LiveKitTransport implements Transport {\n  private room?: Room;\n  private callbacks?: TransportCallbacks;\n  private pendingMicTrack?: Promise<LocalAudioTrack>;\n\n  prepareMicrophone(options: { inputDeviceId?: string }): void {\n    const pending = captureMicrophone(options.inputDeviceId);\n    // A denial surfaces where connect() awaits the track, never as an\n    // unhandled rejection here.\n    pending.catch(() => undefined);\n    this.pendingMicTrack = pending;\n  }\n\n  async connect(sessionToken: SessionToken, options: TransportConnectOptions): Promise<void> {\n    this.callbacks = options.callbacks;\n    const room = new Room({\n      audioCaptureDefaults: {\n        deviceId: options.inputDeviceId,\n        echoCancellation: true,\n        noiseSuppression: true,\n        autoGainControl: true,\n      },\n    });\n    this.room = room;\n\n    room.on(RoomEvent.DataReceived, (payload, _participant, _kind, topic) => {\n      if (topic !== AGENT_EVENT_TOPIC) {\n        return;\n      }\n      let message: unknown;\n      try {\n        message = JSON.parse(new TextDecoder().decode(payload));\n      } catch {\n        return;\n      }\n      if (hasMessageType(message)) {\n        this.callbacks?.onAgentEvent(message as AgentSessionMessage);\n      }\n    });\n\n    const AGENT_STATE_ATTRIBUTE = \"lk.agent.state\";\n    room.on(RoomEvent.ParticipantAttributesChanged, (changed, participant) => {\n      const state = changed[AGENT_STATE_ATTRIBUTE];\n      if (!participant.isLocal && state) {\n        this.callbacks?.onAgentState(state);\n      }\n    });\n\n    room.on(RoomEvent.TrackSubscribed, (track: RemoteTrack) => {\n      if (track.kind === Track.Kind.Audio && track.mediaStreamTrack) {\n        this.callbacks?.onOutputStream(new MediaStream([track.mediaStreamTrack]));\n      }\n    });\n\n    // Both sides' transcripts ride LiveKit's built-in transcription streams:\n    // agent segments are playout-synced delta streams (final on stream close —\n    // an interrupted segment closes containing only the spoken text); user STT\n    // segments are re-published per update with an explicit final attribute,\n    // sent on behalf of the user, so sender identity distinguishes the roles.\n    room.registerTextStreamHandler(\"lk.transcription\", async (reader, participantInfo) => {\n      const attributes = reader.info.attributes ?? {};\n      const segmentId = attributes[\"lk.segment_id\"];\n      if (!segmentId) {\n        return;\n      }\n      const role =\n        participantInfo.identity === room.localParticipant.identity ? \"user\" : \"agent\";\n      const finalAttribute = attributes[\"lk.transcription_final\"] === \"true\";\n      let text = \"\";\n      let aborted = false;\n      try {\n        for await (const delta of reader) {\n          text += delta;\n          this.callbacks?.onTranscription({ segmentId, role, text, final: false });\n        }\n      } catch {\n        aborted = true; // interrupted mid-stream; what arrived is what was spoken\n      }\n      if (text) {\n        const final = role === \"agent\" ? true : finalAttribute && !aborted;\n        this.callbacks?.onTranscription({ segmentId, role, text, final });\n      }\n    });\n\n    room.on(RoomEvent.Reconnecting, () => {\n      this.callbacks?.onConnectionState(\"reconnecting\");\n    });\n    room.on(RoomEvent.Reconnected, () => {\n      // A full reconnect replays the roster from the join response before this\n      // event; an agent still missing here hung up while we were away.\n      if (room.remoteParticipants.size === 0) {\n        this.handleAgentLeft(room);\n        return;\n      }\n      this.callbacks?.onConnectionState(\"connected\");\n    });\n    room.on(RoomEvent.Disconnected, (reason?: DisconnectReason) => {\n      this.callbacks?.onConnectionState(\n        \"disconnected\",\n        reason === undefined ? undefined : DisconnectReason[reason],\n      );\n      // Remote-initiated ends never go through disconnect(); drop the refs here\n      // so getRoom() honors its ended-means-undefined contract and the room\n      // can be collected.\n      if (this.room === room) {\n        this.room = undefined;\n        this.callbacks = undefined;\n      }\n    });\n    // An agent-side hangup ends the worker without closing the room; the\n    // agent participant leaving IS the end-of-call signal for the client.\n    // A full reconnect also clears the roster this way, and can do so while\n    // room.state still reads Connected (Reconnecting is only set right after),\n    // so defer one microtask and require a room that is genuinely connected.\n    room.on(RoomEvent.ParticipantDisconnected, () => {\n      if (room.remoteParticipants.size === 0) {\n        queueMicrotask(() => {\n          if (room.state === ConnectionState.Connected && room.remoteParticipants.size === 0) {\n            this.handleAgentLeft(room);\n          }\n        });\n      }\n    });\n\n    let micTrack: LocalAudioTrack | undefined;\n    if (options.microphone !== false) {\n      // Usually settled long before this point: the capture starts inside the\n      // user gesture (prepareMicrophone). Awaited before joining the room so a\n      // denial fails the start without ever dispatching an agent. The promise\n      // stays in pendingMicTrack until the room owns the track, so disconnect()\n      // can release it after any failure in between.\n      const pending = this.pendingMicTrack ?? captureMicrophone(options.inputDeviceId);\n      this.pendingMicTrack = pending;\n      micTrack = await pending;\n    }\n\n    try {\n      await room.connect(sessionToken.livekit_url, sessionToken.token);\n    } catch (cause) {\n      throw new FishAgentError(\"connection_failed\", \"Could not establish the realtime session\", {\n        cause,\n      });\n    }\n    // The attribute is sticky: if the agent joined and set its state before us,\n    // no change event will fire — replay the current value once.\n    for (const participant of room.remoteParticipants.values()) {\n      const state = participant.attributes?.[AGENT_STATE_ATTRIBUTE];\n      if (state) {\n        this.callbacks?.onAgentState(state);\n      }\n    }\n    if (micTrack) {\n      await room.localParticipant.publishTrack(micTrack);\n      this.pendingMicTrack = undefined;\n      this.publishInputStream();\n    }\n  }\n\n  private handleAgentLeft(room: Room): void {\n    this.callbacks?.onConnectionState(\"disconnected\", \"AGENT_LEFT\");\n    void room.disconnect();\n  }\n\n  async disconnect(): Promise<void> {\n    // A capture the room never took ownership of (start failed between the\n    // gesture-time capture and publish) is invisible to room.disconnect().\n    const pendingMic = this.pendingMicTrack;\n    this.pendingMicTrack = undefined;\n    void pendingMic?.then((track) => track.stop()).catch(() => undefined);\n    const room = this.room;\n    this.room = undefined;\n    this.callbacks = undefined;\n    await room?.disconnect();\n  }\n\n  getRoom(): Room | undefined {\n    return this.room;\n  }\n\n  async setMicEnabled(enabled: boolean): Promise<void> {\n    await this.room?.localParticipant.setMicrophoneEnabled(enabled);\n    if (enabled) {\n      // After a mic-less connect the first enable captures the track late;\n      // re-wiring an already-published track is a harmless replace.\n      this.publishInputStream();\n    }\n  }\n\n  async setInputDevice(deviceId: string): Promise<void> {\n    const room = this.room;\n    if (!room) {\n      return;\n    }\n    const previous = room.getActiveDevice(\"audioinput\");\n    let switched: boolean;\n    try {\n      // exact: a wrong id must fail loudly, not silently fall back to another mic.\n      switched = await room.switchActiveDevice(\"audioinput\", deviceId, true);\n    } catch (error) {\n      // livekit stops the current track before acquiring the new device, so a\n      // failed pick would otherwise leave the call silently mic-dead — switch\n      // back before surfacing the failure. livekit reports \"default\" until the\n      // real captured id is known; that pseudo-id only resolves on Chrome, so\n      // restore it non-exactly and let other browsers fall back to any mic.\n      if (previous !== undefined) {\n        await room\n          .switchActiveDevice(\"audioinput\", previous, previous !== \"default\")\n          .catch(() => undefined);\n        this.publishInputStream();\n      }\n      throw error;\n    }\n    if (!switched) {\n      throw new FishAgentError(\n        \"device_change_failed\",\n        \"Could not activate the requested microphone\",\n      );\n    }\n    // A live switch replaces the publication's MediaStreamTrack — re-wire\n    // analysis. While muted, livekit defers the swap to the next unmute and\n    // setMicEnabled re-wires then.\n    this.publishInputStream();\n  }\n\n  /** Local tracks already wired to re-emit their stream when livekit restarts them. */\n  private readonly restartWiredTracks = new WeakSet<object>();\n\n  private publishInputStream(): void {\n    const room = this.room;\n    if (!room) {\n      return;\n    }\n    for (const publication of room.localParticipant.audioTrackPublications.values()) {\n      const track = publication.track;\n      if (track && !this.restartWiredTracks.has(track)) {\n        this.restartWiredTracks.add(track);\n        // Restarts replace the MediaStreamTrack without any Room-level event —\n        // livekit's devicechange auto-recovery and getRoom() escape-hatch\n        // switches would otherwise leave input analysis on a dead track.\n        track.on(TrackEvent.Restarted, () => this.publishInputStream());\n      }\n      const mediaStreamTrack = track?.mediaStreamTrack;\n      if (mediaStreamTrack) {\n        this.callbacks?.onInputStream(new MediaStream([mediaStreamTrack]));\n      }\n    }\n  }\n\n  async sendClientEvent(message: ClientSessionMessage): Promise<void> {\n    if (!this.room) {\n      throw new FishAgentError(\"connection_failed\", \"Session is not connected\");\n    }\n    const payload = new TextEncoder().encode(JSON.stringify(message));\n    await this.room.localParticipant.publishData(payload, {\n      reliable: true,\n      topic: CLIENT_EVENT_TOPIC,\n    });\n  }\n}\n","// Public surface. Transport-neutral except the deliberate escape hatch\n// `AgentSession.getRoom()`, whose return type is livekit-client's `Room`.\nexport { AgentSession, type AgentSessionOptions } from \"./session/agentSession.js\";\nexport { DEFAULT_SERVER_URL } from \"./sessionToken.js\";\nexport { FishAgentError, type FishAgentErrorCode } from \"./errors.js\";\nexport type {\n  AgentMode,\n  AgentResponseDeltaEvent,\n  AgentResponseEvent,\n  AgentSessionCallbacks,\n  AgentSessionEvents,\n  ConversationMessage,\n  EndReason,\n  SessionStatus,\n  ToolCallCompletedEvent,\n  ToolCallFailedEvent,\n  ToolCallStartedEvent,\n  TranscriptSegment,\n  UserTranscriptEvent,\n} from \"./events.js\";\nexport type { ClientToolHandler } from \"./session/toolDispatcher.js\";\nexport { MAX_CLIENT_TOOL_RESULT_BYTES } from \"./session/toolDispatcher.js\";\nexport type {\n  AgentSessionCreateRequest,\n  SessionOverrides,\n  SessionToken,\n  ToolCallSource,\n} from \"@fishaudio/agent-protocol\";\n","import { FishAgentError } from \"../errors.js\";\n\n/** Throws `device_change_failed` where the browser cannot route audio output (`setSinkId`). */\nexport function assertOutputSelectionSupported(): void {\n  if (typeof HTMLMediaElement === \"undefined\" || !(\"setSinkId\" in HTMLMediaElement.prototype)) {\n    throw new FishAgentError(\n      \"device_change_failed\",\n      \"This browser does not support selecting an audio output device\",\n    );\n  }\n}\n\n/** Agent audio playback via a detached <audio> element (browser only; inert elsewhere). */\nexport class AudioOutput {\n  private element?: HTMLAudioElement;\n  private volume = 1;\n\n  private ensureElement(): HTMLAudioElement | undefined {\n    if (typeof document === \"undefined\") {\n      return undefined;\n    }\n    if (!this.element) {\n      this.element = document.createElement(\"audio\");\n      this.element.autoplay = true;\n      // Same-effect attribute as playsInline for iOS Safari without DOM typings noise.\n      this.element.setAttribute(\"playsinline\", \"\");\n      this.element.volume = this.volume;\n    }\n    return this.element;\n  }\n\n  setStream(stream: MediaStream): void {\n    const element = this.ensureElement();\n    if (!element) {\n      return;\n    }\n    element.srcObject = stream;\n    void element.play().catch(() => {\n      // Autoplay policy: playback stays pending until startAudio() runs in a user gesture.\n    });\n  }\n\n  /** Call from a user gesture to satisfy browser autoplay policies. */\n  async startAudio(): Promise<void> {\n    await this.element?.play();\n  }\n\n  /**\n   * Route playback to an output device. The element is created on demand so\n   * the browser validates the device id right here, even before the agent's\n   * audio arrives. Rejects with `device_change_failed` where output selection\n   * is unsupported or the device cannot be used.\n   */\n  async setSinkId(sinkId: string): Promise<void> {\n    assertOutputSelectionSupported();\n    const element = this.ensureElement();\n    try {\n      await element?.setSinkId(sinkId);\n    } catch (error) {\n      throw new FishAgentError(\n        \"device_change_failed\",\n        \"Could not switch to the requested audio output device\",\n        { cause: error },\n      );\n    }\n  }\n\n  setVolume(volume: number): void {\n    this.volume = Math.min(1, Math.max(0, volume));\n    if (this.element) {\n      this.element.volume = this.volume;\n    }\n  }\n\n  getVolume(): number {\n    return this.volume;\n  }\n\n  dispose(): void {\n    if (this.element) {\n      this.element.srcObject = null;\n      this.element.remove();\n      this.element = undefined;\n    }\n  }\n}\n","/**\n * WebAudio analyser over a MediaStream, feeding visualizers (levels + FFT)\n * without exposing any transport object. Browser only; every accessor\n * degrades to silence when WebAudio is unavailable.\n */\nexport class AudioStreamAnalyser {\n  private context?: AudioContext;\n  private analyser?: AnalyserNode;\n  private source?: MediaStreamAudioSourceNode;\n\n  setStream(stream: MediaStream): void {\n    if (typeof AudioContext === \"undefined\") {\n      return;\n    }\n    this.context ??= new AudioContext();\n    this.analyser ??= this.context.createAnalyser();\n    this.source?.disconnect();\n    this.source = this.context.createMediaStreamSource(stream);\n    this.source.connect(this.analyser);\n    this.resume();\n  }\n\n  /** AudioContexts start suspended under autoplay policies; safe to call any time. */\n  resume(): void {\n    if (this.context?.state === \"suspended\") {\n      void this.context.resume().catch(() => {});\n    }\n  }\n\n  /** RMS level in [0, 1]. */\n  getVolume(): number {\n    if (!this.analyser) {\n      return 0;\n    }\n    const samples = new Uint8Array(this.analyser.fftSize);\n    this.analyser.getByteTimeDomainData(samples);\n    let sumOfSquares = 0;\n    for (const sample of samples) {\n      const centered = (sample - 128) / 128;\n      sumOfSquares += centered * centered;\n    }\n    return Math.sqrt(sumOfSquares / samples.length);\n  }\n\n  getFrequencyData(): Uint8Array {\n    if (!this.analyser) {\n      return new Uint8Array(0);\n    }\n    const data = new Uint8Array(this.analyser.frequencyBinCount);\n    this.analyser.getByteFrequencyData(data);\n    return data;\n  }\n\n  dispose(): void {\n    this.source?.disconnect();\n    this.analyser?.disconnect();\n    void this.context?.close().catch(() => {});\n    this.source = undefined;\n    this.analyser = undefined;\n    this.context = undefined;\n  }\n}\n","// Type-only: the transport implementation still loads lazily (see start()).\nimport type { Room } from \"livekit-client\";\nimport type {\n  AgentSessionMessage,\n  ClientSessionMessage,\n  SessionToken,\n} from \"@fishaudio/agent-protocol\";\nimport { assertOutputSelectionSupported, AudioOutput } from \"../audio/output.js\";\nimport { AudioStreamAnalyser } from \"../audio/analysis.js\";\nimport { FishAgentError } from \"../errors.js\";\nimport {\n  AGENT_SESSION_EVENT_NAMES,\n  type AgentMode,\n  type AgentSessionCallbacks,\n  type AgentSessionEvents,\n  type EndReason,\n  type SessionStatus,\n  type TranscriptSegment,\n} from \"../events.js\";\nimport { resolveSessionToken, type SessionRequestOptions } from \"../sessionToken.js\";\nimport type {\n  TranscriptionSegmentUpdate,\n  Transport,\n  TransportConnectionState,\n  TransportFactory,\n} from \"../transport/types.js\";\nimport { TypedEmitter } from \"./emitter.js\";\nimport {\n  ClientToolDispatcher,\n  DEFAULT_CLIENT_TOOL_TIMEOUT_MS,\n  type ClientToolHandler,\n} from \"./toolDispatcher.js\";\nimport { WakeLockHolder } from \"./wakeLock.js\";\n\nexport interface AgentSessionOptions extends SessionRequestOptions {\n  clientTools?: Record<string, ClientToolHandler>;\n  clientToolTimeoutMs?: number;\n  /**\n   * Capture the microphone on start. Default true. Set false to join without\n   * requesting it (text-first UIs); the first `setMicMuted(false)` then\n   * captures — the permission prompt happens there, so call it from a user\n   * gesture.\n   */\n  microphone?: boolean;\n  audio?: {\n    inputDeviceId?: string;\n    /** Playback device (`setSinkId`); start rejects with `device_change_failed`\n     * where output selection is unsupported (before creating a server session)\n     * or the device cannot be used (before connecting). */\n    outputDeviceId?: string;\n  };\n  /**\n   * Hold a screen wake lock while the session is live, so long calls survive\n   * the phone trying to sleep. Default true; denial is silent. Set false to\n   * leave screen policy to the page.\n   */\n  wakeLock?: boolean;\n  callbacks?: Partial<AgentSessionCallbacks>;\n}\n\n/** How long after connect the agent participant gets to show up before the\n * session gives up — normal joins land within a couple of seconds. */\nconst AGENT_JOIN_TIMEOUT_MS = 15_000;\n\n/** The runtime tears the room down right after announcing `session.ended`;\n * if that teardown signal never lands, end locally with the announced reason. */\nconst SESSION_ENDED_GRACE_MS = 10_000;\n\n/** `session.ended` reasons this SDK understands; the reason set is additive,\n * so an announcement carrying an unrecognized value is ignored by contract\n * and the transport disconnect signals decide instead. */\nconst ANNOUNCED_END_REASONS: ReadonlySet<string> = new Set<EndReason>([\n  \"user_hangup\",\n  \"agent_hangup\",\n  \"conversation_timeout\",\n  \"escalated\",\n]);\n\nconst EVENT_NAMES = new Set<string>(AGENT_SESSION_EVENT_NAMES);\n\n/**\n * Maps the `callbacks` shorthand (`onUserTranscript`) to event names\n * (`userTranscript`). Unknown keys throw: silently subscribing to a\n * non-existent event would hide typos and the bare event names people\n * reach for first (`userTranscript` instead of `onUserTranscript`).\n */\nfunction resolveCallbackSubscriptions(\n  callbacks: Partial<AgentSessionCallbacks> | undefined,\n): Array<[keyof AgentSessionEvents, unknown]> {\n  const subscriptions: Array<[keyof AgentSessionEvents, unknown]> = [];\n  for (const [key, callback] of Object.entries(callbacks ?? {})) {\n    if (callback === undefined) {\n      continue;\n    }\n    const event = /^on[A-Z]/.test(key) ? key.charAt(2).toLowerCase() + key.slice(3) : undefined;\n    if (event === undefined || !EVENT_NAMES.has(event)) {\n      const expected = AGENT_SESSION_EVENT_NAMES.map(\n        (name) => `on${name.charAt(0).toUpperCase()}${name.slice(1)}`,\n      ).join(\", \");\n      throw new TypeError(\n        `Unknown callback \"${key}\". Callback keys are event names prefixed with \"on\": ${expected}.`,\n      );\n    }\n    if (typeof callback !== \"function\") {\n      throw new TypeError(`Callback \"${key}\" must be a function, got ${typeof callback}.`);\n    }\n    subscriptions.push([event as keyof AgentSessionEvents, callback]);\n  }\n  return subscriptions;\n}\n\n/** Assigned in AgentSession's static block; bridges startAgentSession to the\n * class-private #startWith so no transport types sit on the public class. */\nlet startWith: (\n  options: AgentSessionOptions,\n  createTransport: TransportFactory,\n) => Promise<AgentSession>;\n\nexport class AgentSession extends TypedEmitter<AgentSessionEvents> {\n  #status: SessionStatus = \"connecting\";\n  #mode: AgentMode = \"listening\";\n  #endReason?: EndReason;\n  /** Reason announced by the runtime over `session.ended`, held until the\n   * transport disconnect (or the grace timer) finalizes the session with it. */\n  #announcedEndReason?: EndReason;\n  #endedGraceTimer?: ReturnType<typeof setTimeout>;\n  #sessionId: string;\n  #micMuted = false;\n  #endedByClient = false;\n  #ending?: Promise<void>;\n\n  #transport!: Transport;\n  #tools!: ClientToolDispatcher;\n  readonly #output = new AudioOutput();\n  readonly #outputAnalyser = new AudioStreamAnalyser();\n  readonly #inputAnalyser = new AudioStreamAnalyser();\n  readonly #wakeLock = new WakeLockHolder();\n\n  /** Text already delivered per agent segment, to derive deltas from full-text updates. */\n  readonly #agentSegments = new Map<string, string>();\n  /** Transcript so far, for late subscribers (getTranscript); upserted per segment. */\n  readonly #transcript: TranscriptSegment[] = [];\n  readonly #transcriptIndex = new Map<string, number>();\n  #typedMessageCount = 0;\n\n  // Data frames reach only participants already in the room — and the worker\n  // registers its handlers while starting up — so typed messages wait here\n  // until the agent publishes its first pipeline state.\n  #agentPresent = false;\n  #pendingMessages: ClientSessionMessage[] = [];\n  #joinTimer?: ReturnType<typeof setTimeout>;\n\n  private constructor(sessionToken: SessionToken) {\n    super();\n    this.#sessionId = sessionToken.session_id;\n  }\n\n  /**\n   * Obtain a session token (if needed), connect, publish the microphone and\n   * hand back a live session. Rejects with FishAgentError on session-request,\n   * permission or connect failures.\n   */\n  static async start(options: AgentSessionOptions): Promise<AgentSession> {\n    // Lazy so the transport implementation (and livekit-client) never loads\n    // in SSR bundles or in tests that inject their own transport.\n    const { LiveKitTransport } = await import(\"../transport/livekit.js\");\n    return AgentSession.#startWith(options, () => new LiveKitTransport());\n  }\n\n  // ---- getters ----\n\n  get sessionId(): string {\n    return this.#sessionId;\n  }\n\n  get status(): SessionStatus {\n    return this.#status;\n  }\n\n  get mode(): AgentMode {\n    return this.#mode;\n  }\n\n  get isSpeaking(): boolean {\n    return this.#mode === \"speaking\";\n  }\n\n  get micMuted(): boolean {\n    return this.#micMuted;\n  }\n\n  get endReason(): EndReason | undefined {\n    return this.#endReason;\n  }\n\n  /**\n   * Snapshot of every transcript segment so far, in conversation order. Seed a\n   * consumer that subscribes after events already fired (e.g. a component\n   * mounted right after start), then apply live events by segmentId.\n   */\n  getTranscript(): TranscriptSegment[] {\n    return this.#transcript.map((segment) => ({ ...segment }));\n  }\n\n  // ---- text & control channel ----\n\n  /** Typed turns get a text-only reply by default (no TTS); pass `audio: true` to have the agent speak this turn. */\n  sendUserMessage(text: string, options?: { audio?: boolean }): void {\n    const trimmed = text.trim();\n    if (!trimmed) {\n      return;\n    }\n    const message: ClientSessionMessage =\n      options?.audio === true\n        ? { type: \"user.message\", text: trimmed }\n        : { type: \"user.message\", text: trimmed, audio: false };\n    if (this.#agentPresent) {\n      this.#sendClientEvent(message);\n    } else {\n      this.#pendingMessages.push(message);\n    }\n    this.#recordTypedUserMessage(trimmed);\n  }\n\n  sendUserActivity(): void {\n    this.#sendClientEvent({ type: \"user.activity\" });\n  }\n\n  interrupt(): void {\n    this.#sendClientEvent({ type: \"user.interrupt\" });\n  }\n\n  registerClientTool(name: string, handler: ClientToolHandler): void {\n    this.#tools.register(name, handler);\n  }\n\n  // ---- audio ----\n\n  /**\n   * After a `microphone: false` start, the first unmute captures and publishes\n   * the microphone — the permission prompt happens here. A denial rejects with\n   * `mic_permission_denied` and the session stays muted.\n   */\n  async setMicMuted(muted: boolean): Promise<void> {\n    const previous = this.#micMuted;\n    this.#micMuted = muted;\n    try {\n      await this.#transport.setMicEnabled(!muted);\n    } catch (error) {\n      this.#micMuted = previous;\n      throw AgentSession.#asMicError(error);\n    }\n  }\n\n  /** Call from a user gesture to satisfy browser autoplay policies. */\n  async startAudio(): Promise<void> {\n    this.#outputAnalyser.resume();\n    this.#inputAnalyser.resume();\n    await this.#output.startAudio();\n  }\n\n  /**\n   * Switch the microphone mid-call. While the mic is not captured yet\n   * (`microphone: false` start, still muted) this records the preference for\n   * the first unmute. Rejects with `device_change_failed` when the device\n   * cannot be activated, switching back to the previous microphone (best\n   * effort — the transport stops the old capture before acquiring the new).\n   */\n  async setInputDevice(deviceId: string): Promise<void> {\n    try {\n      await this.#transport.setInputDevice(deviceId);\n    } catch (error) {\n      throw AgentSession.#asDeviceError(error, \"Could not switch the microphone\");\n    }\n  }\n\n  /**\n   * Route the agent's audio to an output device (`setSinkId`); pass `\"\"` to\n   * return to the default device. Rejects with `device_change_failed` where\n   * the browser does not support output selection (common on mobile browsers)\n   * or the device cannot be used; playback stays on the previous device.\n   */\n  async setOutputDevice(deviceId: string): Promise<void> {\n    await this.#output.setSinkId(deviceId);\n  }\n\n  setOutputVolume(volume: number): void {\n    this.#output.setVolume(volume);\n  }\n\n  getOutputVolume(): number {\n    return this.#outputAnalyser.getVolume();\n  }\n\n  getInputVolume(): number {\n    return this.#inputAnalyser.getVolume();\n  }\n\n  getOutputFrequencyData(): Uint8Array {\n    return this.#outputAnalyser.getFrequencyData();\n  }\n\n  getInputFrequencyData(): Uint8Array {\n    return this.#inputAnalyser.getFrequencyData();\n  }\n\n  /**\n   * Escape hatch: the underlying LiveKit `Room`, for needs the session API\n   * doesn't cover (connection-quality telemetry, publishing extra tracks).\n   * Code using it couples to this SDK's transport choice and livekit-client\n   * version — prefer the session API where one exists. `undefined` after the\n   * session ends, or when a custom transport doesn't expose a room.\n   */\n  getRoom(): Room | undefined {\n    return this.#transport.getRoom?.() as Room | undefined;\n  }\n\n  // ---- lifecycle ----\n\n  end(): Promise<void> {\n    if (this.#ending) {\n      return this.#ending;\n    }\n    if (this.#status === \"ended\") {\n      return Promise.resolve();\n    }\n    const ending = this.#endOnce();\n    this.#ending = ending;\n    void ending.catch(() => {\n      if (this.#ending === ending) {\n        this.#ending = undefined;\n      }\n    });\n    return ending;\n  }\n\n  async #endOnce(): Promise<void> {\n    this.#endedByClient = true;\n    try {\n      // Graceful hangup over the reverse channel; participant departure is the\n      // worker-side fallback if this frame never arrives.\n      await this.#transport.sendClientEvent({ type: \"user.hangup\" });\n    } catch {\n      // best effort\n    }\n    await this.#transport.disconnect();\n    this.#finalize(\"user_hangup\");\n  }\n\n  // ---- internals ----\n\n  static {\n    startWith = (options, createTransport) => AgentSession.#startWith(options, createTransport);\n  }\n\n  static async #startWith(\n    options: AgentSessionOptions,\n    createTransport: TransportFactory,\n  ): Promise<AgentSession> {\n    if (options.audio?.outputDeviceId !== undefined) {\n      // Cheap sync check first, so an unsupported browser fails before a\n      // server session is even created.\n      assertOutputSelectionSupported();\n    }\n    // Validated before anything with side effects: a typo in a callback name\n    // is a programming error and must not cost a microphone prompt or a\n    // server session to discover.\n    const callbackSubscriptions = resolveCallbackSubscriptions(options.callbacks);\n    const transport = createTransport();\n    if (options.microphone !== false) {\n      // Inside the user gesture that called start(), before the token\n      // round-trip pushes getUserMedia out of the gesture's transient\n      // activation (see Transport.prepareMicrophone). A denial surfaces in\n      // transport.connect().\n      transport.prepareMicrophone?.({ inputDeviceId: options.audio?.inputDeviceId });\n    }\n    let sessionToken: SessionToken;\n    let session: AgentSession;\n    try {\n      sessionToken = await resolveSessionToken(options);\n      session = new AgentSession(sessionToken);\n      if (options.audio?.outputDeviceId !== undefined) {\n        // The browser validates the device id here, before anything connects;\n        // rejects like a token failure — no events yet.\n        await session.#output.setSinkId(options.audio.outputDeviceId);\n      }\n    } catch (error) {\n      // The gesture-time capture must not outlive a failed start.\n      await transport.disconnect().catch(() => undefined);\n      throw error;\n    }\n\n    for (const [event, callback] of callbackSubscriptions) {\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      session.on(event, callback as any);\n    }\n\n    session.#transport = transport;\n    if (options.microphone === false) {\n      session.#micMuted = true;\n    }\n    session.#tools = new ClientToolDispatcher(\n      options.clientTools,\n      options.clientToolTimeoutMs ?? DEFAULT_CLIENT_TOOL_TIMEOUT_MS,\n      (message) => transport.sendClientEvent(message),\n      (error) => session.emit(\"error\", error),\n    );\n\n    try {\n      await transport.connect(sessionToken, {\n        inputDeviceId: options.audio?.inputDeviceId,\n        microphone: options.microphone,\n        callbacks: {\n          onAgentEvent: (message) => session.#handleAgentEvent(message),\n          // Mode model: \"listening\" is the default state; the framework's agent\n          // state supplies the thinking edge; speaking follows the playout-synced\n          // transcription segments (open = audible, closed = back to listening).\n          onAgentState: (state) => {\n            // Any published agent state proves the worker's pipeline is up and\n            // its event handlers are registered — the gate for queued messages.\n            session.#handleAgentPresent();\n            if (state === \"thinking\") {\n              session.#setMode(\"thinking\");\n            }\n          },\n          onTranscription: (update) => session.#handleTranscription(update),\n          onOutputStream: (stream) => {\n            session.#output.setStream(stream);\n            session.#outputAnalyser.setStream(stream);\n          },\n          onInputStream: (stream) => session.#inputAnalyser.setStream(stream),\n          onConnectionState: (state, reason) => session.#handleConnectionState(state, reason),\n        },\n      });\n    } catch (error) {\n      // connect() can fail after the room is already joined (e.g. mic capture);\n      // tear the transport down or the room stays connected with no owner.\n      await session.#transport.disconnect().catch(() => undefined);\n      session.#finalize(\"connection_lost\");\n      throw AgentSession.#asStartError(error);\n    }\n\n    if (session.#status === \"connecting\") {\n      session.#setStatus(\"connected\");\n      session.emit(\"connect\", { sessionId: session.#sessionId });\n    }\n    if (session.#status !== \"ended\" && options.wakeLock !== false) {\n      session.#wakeLock.acquire();\n    }\n    if (!session.#agentPresent && session.#status === \"connected\") {\n      session.#joinTimer = setTimeout(() => session.#abandonAgentWait(), AGENT_JOIN_TIMEOUT_MS);\n    }\n    return session;\n  }\n\n  static #asStartError(error: unknown): FishAgentError {\n    if (error instanceof FishAgentError) {\n      return error;\n    }\n    if (error instanceof Error && error.name === \"NotAllowedError\") {\n      return new FishAgentError(\"mic_permission_denied\", \"Microphone permission was denied\", {\n        cause: error,\n      });\n    }\n    return new FishAgentError(\"connection_failed\", \"Could not establish the realtime session\", {\n      cause: error,\n    });\n  }\n\n  static #asMicError(error: unknown): FishAgentError {\n    if (error instanceof FishAgentError) {\n      return error;\n    }\n    if (error instanceof Error && error.name === \"NotAllowedError\") {\n      return new FishAgentError(\"mic_permission_denied\", \"Microphone permission was denied\", {\n        cause: error,\n      });\n    }\n    if (\n      error instanceof Error &&\n      (error.name === \"OverconstrainedError\" || error.name === \"NotFoundError\")\n    ) {\n      // Device/constraint failures surface here both for a preference recorded\n      // while muted and for a first capture with no usable microphone — keep\n      // the device error code, with a message that fits either.\n      return new FishAgentError(\n        \"device_change_failed\",\n        \"No usable microphone could be activated\",\n        { cause: error },\n      );\n    }\n    return new FishAgentError(\"connection_failed\", \"Could not toggle the microphone\", {\n      cause: error,\n    });\n  }\n\n  static #asDeviceError(error: unknown, message: string): FishAgentError {\n    if (error instanceof FishAgentError) {\n      return error;\n    }\n    return new FishAgentError(\"device_change_failed\", message, { cause: error });\n  }\n\n  #sendClientEvent(message: Parameters<Transport[\"sendClientEvent\"]>[0]): void {\n    void this.#transport.sendClientEvent(message).catch((error: unknown) => {\n      this.emit(\n        \"error\",\n        new FishAgentError(\"connection_failed\", \"Failed to send to the agent\", { cause: error }),\n      );\n    });\n  }\n\n  #setStatus(status: SessionStatus): void {\n    if (this.#status !== status) {\n      this.#status = status;\n      this.emit(\"statusChange\", status);\n    }\n  }\n\n  #setMode(mode: AgentMode): void {\n    if (this.#mode !== mode) {\n      this.#mode = mode;\n      this.emit(\"modeChange\", mode);\n    }\n  }\n\n  #handleAgentPresent(): void {\n    if (this.#agentPresent) {\n      return;\n    }\n    this.#agentPresent = true;\n    if (this.#joinTimer) {\n      clearTimeout(this.#joinTimer);\n      this.#joinTimer = undefined;\n    }\n    const pending = this.#pendingMessages;\n    this.#pendingMessages = [];\n    for (const message of pending) {\n      this.#sendClientEvent(message);\n    }\n  }\n\n  #abandonAgentWait(): void {\n    if (this.#status === \"ended\" || this.#agentPresent) {\n      return;\n    }\n    this.emit(\n      \"error\",\n      new FishAgentError(\"connection_failed\", \"The agent did not join the session\"),\n    );\n    void this.#transport.disconnect().catch(() => {});\n    this.#finalize(\"connection_lost\");\n  }\n\n  #handleConnectionState(state: TransportConnectionState, reason?: string): void {\n    if (this.#status === \"ended\") {\n      return;\n    }\n    if (state === \"reconnecting\") {\n      this.#setStatus(\"reconnecting\");\n      return;\n    }\n    if (state === \"connected\") {\n      if (this.#status === \"reconnecting\") {\n        this.#setStatus(\"connected\");\n      }\n      return;\n    }\n    // A `session.ended` announcement from the runtime is authoritative. The\n    // transport signals below are the fallback for ends nobody could announce\n    // (older runtimes, a runtime crash, network loss): AGENT_LEFT = the agent\n    // participant hung up (worker ends without closing the room); room\n    // deletion covers server-forced ends and the duration cap — without the\n    // announcement these are indistinguishable and all read as agent_hangup.\n    const ended = this.#endedByClient\n      ? \"user_hangup\"\n      : (this.#announcedEndReason ??\n        (reason === \"AGENT_LEFT\" || reason === \"ROOM_DELETED\" || reason === \"ROOM_CLOSED\"\n          ? \"agent_hangup\"\n          : \"connection_lost\"));\n    this.#finalize(ended);\n  }\n\n  #finalize(reason: EndReason): void {\n    if (this.#status === \"ended\") {\n      return;\n    }\n    if (this.#joinTimer) {\n      clearTimeout(this.#joinTimer);\n      this.#joinTimer = undefined;\n    }\n    if (this.#endedGraceTimer) {\n      clearTimeout(this.#endedGraceTimer);\n      this.#endedGraceTimer = undefined;\n    }\n    this.#endReason = reason;\n    this.#setStatus(\"ended\");\n    this.#wakeLock.release();\n    this.#output.dispose();\n    this.#outputAnalyser.dispose();\n    this.#inputAnalyser.dispose();\n    this.emit(\"disconnect\", { reason });\n  }\n\n  #handleSessionEnded(reason: string): void {\n    if (this.#status === \"ended\" || this.#announcedEndReason !== undefined) {\n      return;\n    }\n    if (!ANNOUNCED_END_REASONS.has(reason)) {\n      // Forward compatibility: an unrecognized reason voids the announcement;\n      // the transport disconnect signals decide instead.\n      return;\n    }\n    this.#announcedEndReason = reason as EndReason;\n    const announced = this.#announcedEndReason;\n    this.#endedGraceTimer = setTimeout(() => {\n      void this.#transport.disconnect().catch(() => {});\n      this.#finalize(announced);\n    }, SESSION_ENDED_GRACE_MS);\n  }\n\n  #handleAgentEvent(message: AgentSessionMessage): void {\n    switch (message.type) {\n      case \"client_tool.call\":\n        void this.#tools.dispatch(message);\n        return;\n      case \"tool.started\":\n        this.emit(\"toolCallStarted\", {\n          callId: message.callId,\n          toolName: message.toolName,\n          source: message.toolSource,\n          input: message.input,\n          inputTruncated: message.inputTruncated === true,\n        });\n        return;\n      case \"tool.completed\":\n        this.emit(\"toolCallCompleted\", {\n          callId: message.callId,\n          toolName: message.toolName,\n          source: message.toolSource,\n          output: message.output,\n          outputTruncated: message.outputTruncated === true,\n        });\n        return;\n      case \"tool.failed\":\n        this.emit(\"toolCallFailed\", {\n          callId: message.callId,\n          toolName: message.toolName,\n          source: message.toolSource,\n          error: message.error,\n        });\n        return;\n      case \"session.ended\":\n        this.#handleSessionEnded(message.reason);\n        return;\n      case \"error\":\n        this.emit(\n          \"error\",\n          message.code === \"provider_error\"\n            ? new FishAgentError(\"provider_error\", \"An upstream provider failed during the session\")\n            : new FishAgentError(\"internal_error\", \"The agent runtime hit an internal error\"),\n        );\n        return;\n      default:\n        // Forward compatibility: unknown message types are ignored by contract.\n        return;\n    }\n  }\n\n  #recordSegment(role: \"user\" | \"agent\", segmentId: string, text: string, final: boolean): void {\n    const key = `${role}:${segmentId}`;\n    const index = this.#transcriptIndex.get(key);\n    if (index === undefined) {\n      this.#transcriptIndex.set(key, this.#transcript.length);\n      this.#transcript.push({ segmentId, role, text, final });\n    } else {\n      this.#transcript[index] = { segmentId, role, text, final };\n    }\n  }\n\n  #handleTranscription(update: TranscriptionSegmentUpdate): void {\n    if (update.role === \"user\") {\n      this.#recordSegment(\"user\", update.segmentId, update.text, update.final);\n      this.emit(\"userTranscript\", {\n        segmentId: update.segmentId,\n        text: update.text,\n        final: update.final,\n      });\n      if (update.final) {\n        this.emit(\"message\", { role: \"user\", text: update.text });\n      }\n      return;\n    }\n    // Agent speech: playout-synced, so streaming text means the agent is audibly\n    // speaking; the segment closing means playout finished — back to listening.\n    this.#setMode(update.final ? \"listening\" : \"speaking\");\n    this.#recordSegment(\"agent\", update.segmentId, update.text, update.final);\n    const previous = this.#agentSegments.get(update.segmentId) ?? \"\";\n    const delta = update.text.startsWith(previous) ? update.text.slice(previous.length) : update.text;\n    this.#agentSegments.set(update.segmentId, update.text);\n    if (delta) {\n      this.emit(\"agentResponseDelta\", { segmentId: update.segmentId, delta, text: update.text });\n    }\n    if (update.final) {\n      this.#agentSegments.delete(update.segmentId);\n      this.emit(\"agentResponse\", { segmentId: update.segmentId, text: update.text });\n      this.emit(\"message\", { role: \"agent\", text: update.text });\n    }\n  }\n\n  // Typed messages are not echoed by the server; the sender finalizes its own bubble.\n  #recordTypedUserMessage(text: string): void {\n    const segmentId = `typed_${++this.#typedMessageCount}`;\n    this.#recordSegment(\"user\", segmentId, text, true);\n    this.emit(\"userTranscript\", { segmentId, text, final: true });\n    this.emit(\"message\", { role: \"user\", text });\n  }\n}\n\n/** Internal seam: identical to AgentSession.start but with an injectable transport. */\nexport function startAgentSession(\n  options: AgentSessionOptions,\n  createTransport: TransportFactory,\n): Promise<AgentSession> {\n  return startWith(options, createTransport);\n}\n","import type { ToolCallSource } from \"@fishaudio/agent-protocol\";\nimport type { FishAgentError } from \"./errors.js\";\n\nexport type SessionStatus = \"connecting\" | \"connected\" | \"reconnecting\" | \"ended\";\n\nexport type AgentMode = \"listening\" | \"thinking\" | \"speaking\";\n\n/**\n * Why the session ended. `connection_lost` is client-derived (reconnection\n * failed, the agent never joined, or the call dropped before the runtime could\n * say why); every other value is the runtime's own reason, delivered over the\n * protocol's `session.ended` announcement — or, against an older runtime that\n * never sends one, inferred from transport signals (where every server-side\n * end surfaces as `agent_hangup`).\n */\nexport type EndReason =\n  | \"user_hangup\"\n  | \"agent_hangup\"\n  | \"conversation_timeout\"\n  | \"escalated\"\n  | \"connection_lost\";\n\nexport interface UserTranscriptEvent {\n  /** Groups updates of one utterance; interim updates replace, they never append. */\n  segmentId: string;\n  /** Full text of the utterance so far. */\n  text: string;\n  final: boolean;\n}\n\nexport interface AgentResponseDeltaEvent {\n  /** Groups streaming updates of one agent speech segment. */\n  segmentId: string;\n  delta: string;\n  /** Accumulated segment text including this delta. */\n  text: string;\n}\n\nexport interface AgentResponseEvent {\n  segmentId: string;\n  text: string;\n}\n\n/** One transcript segment; `text` is the whole segment so far, refined in place. */\nexport interface TranscriptSegment {\n  segmentId: string;\n  role: \"user\" | \"agent\";\n  text: string;\n  final: boolean;\n}\n\nexport interface ConversationMessage {\n  role: \"user\" | \"agent\";\n  text: string;\n}\n\n/**\n * Tool-call lifecycle, forwarded for every tool the agent runs (including client\n * tools, which complete once this client returns their result). `callId` pairs one\n * started event with exactly one completed/failed event; terminal events repeat\n * name/source so a listener attached mid-call can still render a full item.\n * Emission is a session-creation option (`toolEvents`/`tool_events`, default true).\n */\nexport interface ToolCallStartedEvent {\n  callId: string;\n  toolName: string;\n  source: ToolCallSource;\n  /** Tool arguments as a JSON string; parse only when `inputTruncated` is false. */\n  input: string;\n  inputTruncated: boolean;\n}\n\nexport interface ToolCallCompletedEvent {\n  callId: string;\n  toolName: string;\n  source: ToolCallSource;\n  /** Tool result as a JSON string; parse only when `outputTruncated` is false. */\n  output: string;\n  outputTruncated: boolean;\n}\n\nexport interface ToolCallFailedEvent {\n  callId: string;\n  toolName: string;\n  source: ToolCallSource;\n  error: string;\n}\n\nexport interface AgentSessionEvents {\n  connect: (event: { sessionId: string }) => void;\n  disconnect: (event: { reason: EndReason }) => void;\n  statusChange: (status: SessionStatus) => void;\n  modeChange: (mode: AgentMode) => void;\n  userTranscript: (event: UserTranscriptEvent) => void;\n  agentResponseDelta: (event: AgentResponseDeltaEvent) => void;\n  agentResponse: (event: AgentResponseEvent) => void;\n  /** Finalized messages only, in conversation order — the \"just give me a chat log\" event. */\n  message: (message: ConversationMessage) => void;\n  toolCallStarted: (event: ToolCallStartedEvent) => void;\n  toolCallCompleted: (event: ToolCallCompletedEvent) => void;\n  toolCallFailed: (event: ToolCallFailedEvent) => void;\n  error: (error: FishAgentError) => void;\n}\n\nexport type AgentSessionCallbacks = {\n  [K in keyof AgentSessionEvents as `on${Capitalize<K>}`]: AgentSessionEvents[K];\n};\n\n/** Every event name, for runtime validation of the `callbacks` shorthand. */\nexport const AGENT_SESSION_EVENT_NAMES = [\n  \"connect\",\n  \"disconnect\",\n  \"statusChange\",\n  \"modeChange\",\n  \"userTranscript\",\n  \"agentResponseDelta\",\n  \"agentResponse\",\n  \"message\",\n  \"toolCallStarted\",\n  \"toolCallCompleted\",\n  \"toolCallFailed\",\n  \"error\",\n] as const satisfies readonly (keyof AgentSessionEvents)[];\n\n// Compile-time check that the list above stays exhaustive.\ntype MissingEventName = Exclude<keyof AgentSessionEvents, (typeof AGENT_SESSION_EVENT_NAMES)[number]>;\nconst _assertEventNamesExhaustive: MissingEventName extends never ? true : never = true;\nvoid _assertEventNamesExhaustive;\n","import {\n  SESSION_TRANSPORT_LIVEKIT,\n  type AgentSessionCreateRequest,\n  type SessionLanguage,\n  type SessionOverrides,\n  type SessionToken,\n} from \"@fishaudio/agent-protocol\";\nimport { FishAgentError } from \"./errors.js\";\n\nexport const SDK_VERSION = \"0.0.1\";\nexport const DEFAULT_SERVER_URL = \"https://api.fish.audio\";\n\nconst SUPPORTED_TRANSPORTS: string[] = [SESSION_TRANSPORT_LIVEKIT];\n\nexport interface SessionRequestOptions {\n  /** Public agent: the SDK creates the session directly against the Fish API. */\n  agentId?: string;\n  /** Session token created by the host backend, passed through verbatim. */\n  sessionToken?: SessionToken;\n  serverUrl?: string;\n  /** Sugar for `overrides.language`; an explicit override wins. Pair with a voice in that language. */\n  language?: SessionLanguage;\n  /**\n   * IANA timezone for the agent's sense of local time in this session. Omit to\n   * use the browser's timezone (sent automatically as a hint). With\n   * `sessionToken` auth the host backend chooses instead, via `timezone` on its\n   * session-creation call.\n   */\n  timezone?: string;\n  /**\n   * The agent knows the current date and time by default. Set false to withhold\n   * both from this session's prompt. With `sessionToken` auth the host backend\n   * chooses instead, via `world_context` on its session-creation call.\n   */\n  worldContext?: boolean;\n  /** Per-session config overrides; each field must be allow-listed on the agent. */\n  overrides?: SessionOverrides;\n  /** `{{name}}` template values for the agent's prompt and first message. */\n  dynamicVariables?: Record<string, string | number | boolean>;\n  endUserId?: string;\n  /** Caller-owned tag, stored and returned verbatim; the platform never interprets it. */\n  metadata?: Record<string, unknown>;\n  /**\n   * Stream the agent's tool-call lifecycle (toolCallStarted/Completed/Failed\n   * events, with payloads) to this session. Default true; set false when tool\n   * data must stay hidden from this client. With `sessionToken` auth the host\n   * backend chooses instead, via `tool_events` on its session-creation call.\n   */\n  toolEvents?: boolean;\n}\n\nexport function validateSessionToken(sessionToken: SessionToken): SessionToken {\n  const transport: string = sessionToken.transport;\n  if (!SUPPORTED_TRANSPORTS.includes(transport)) {\n    throw new FishAgentError(\n      \"unsupported_transport\",\n      `Session token uses transport \"${transport}\" which this SDK version does not support; ` +\n        \"please upgrade @fishaudio/agent-client\",\n    );\n  }\n  const expiresAt = Date.parse(sessionToken.expires_at);\n  if (!Number.isNaN(expiresAt) && expiresAt <= Date.now()) {\n    throw new FishAgentError(\n      \"session_expired\",\n      \"The session token's join deadline has passed; request a new session\",\n    );\n  }\n  return sessionToken;\n}\n\nexport async function resolveSessionToken(options: SessionRequestOptions): Promise<SessionToken> {\n  const provided = [options.agentId, options.sessionToken].filter((value) => value !== undefined);\n  if (provided.length !== 1) {\n    throw new TypeError(\n      \"Exactly one of `agentId` or `sessionToken` must be provided to start a session\",\n    );\n  }\n\n  if (options.sessionToken) {\n    return validateSessionToken(options.sessionToken);\n  }\n  return validateSessionToken(await createPublicSession(options));\n}\n\n// The browser knows the end user's real timezone; without the hint the\n// platform would have to guess UTC.\nfunction detectClientTimezone(): string | undefined {\n  try {\n    return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nasync function createPublicSession(options: SessionRequestOptions): Promise<SessionToken> {\n  const serverUrl = (options.serverUrl ?? DEFAULT_SERVER_URL).replace(/\\/$/, \"\");\n  const clientTimezone = detectClientTimezone();\n  const overrides: SessionOverrides | undefined =\n    options.language || options.overrides\n      ? { ...(options.language ? { language: options.language } : {}), ...options.overrides }\n      : undefined;\n  const request: AgentSessionCreateRequest = {\n    agent_id: options.agentId as string,\n    ...(options.timezone ? { timezone: options.timezone } : {}),\n    ...(clientTimezone ? { client_timezone: clientTimezone } : {}),\n    ...(options.worldContext !== undefined ? { world_context: options.worldContext } : {}),\n    ...(overrides ? { overrides } : {}),\n    ...(options.dynamicVariables ? { dynamic_variables: options.dynamicVariables } : {}),\n    ...(options.endUserId ? { end_user_id: options.endUserId } : {}),\n    ...(options.metadata ? { metadata: options.metadata } : {}),\n    ...(options.toolEvents !== undefined ? { tool_events: options.toolEvents } : {}),\n  };\n\n  let response: Response;\n  try {\n    response = await fetch(`${serverUrl}/v1/agent/sessions`, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        \"X-Fish-SDK\": `agent-client/${SDK_VERSION}`,\n      },\n      body: JSON.stringify(request),\n    });\n  } catch (cause) {\n    throw new FishAgentError(\"session_request_failed\", \"Could not reach the session endpoint\", {\n      cause,\n    });\n  }\n\n  if (!response.ok) {\n    throw await createSessionError(response);\n  }\n  return (await response.json()) as SessionToken;\n}\n\nasync function createSessionError(response: Response): Promise<FishAgentError> {\n  let message = `Session request failed with HTTP ${response.status}`;\n  let code: string | undefined;\n  try {\n    const body = (await response.json()) as { message?: string; error?: string; code?: string };\n    message = body.message ?? body.error ?? message;\n    code = body.code;\n  } catch {\n    // keep the status-based message\n  }\n  const options = { statusCode: response.status };\n  // 409 alone is ambiguous (the server also uses it for e.g. an unpublished\n  // agent); only the coded arm means \"this SDK is too old\".\n  if (response.status === 409 && code === \"unsupported_transport\") {\n    return new FishAgentError(\n      \"unsupported_transport\",\n      `${message}; please upgrade @fishaudio/agent-client`,\n      options,\n    );\n  }\n  if (response.status === 403) {\n    return /origin/i.test(message)\n      ? new FishAgentError(\"origin_forbidden\", message, options)\n      : new FishAgentError(\"agent_not_public\", message, options);\n  }\n  return new FishAgentError(\"session_request_failed\", message, options);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\ntype Listener = (...args: any[]) => void;\n\n/** Minimal typed emitter; listener errors are isolated so one bad consumer can't break the session. */\nexport class TypedEmitter<Events extends Record<keyof Events, Listener>> {\n  private readonly listeners = new Map<keyof Events, Set<Listener>>();\n\n  on<K extends keyof Events>(event: K, listener: Events[K]): this {\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);\n    return this;\n  }\n\n  off<K extends keyof Events>(event: K, listener: Events[K]): this {\n    this.listeners.get(event)?.delete(listener);\n    return this;\n  }\n\n  once<K extends keyof Events>(event: K, listener: Events[K]): this {\n    const wrapper = ((...args: Parameters<Events[K]>) => {\n      this.off(event, wrapper);\n      listener(...args);\n    }) as Events[K];\n    return this.on(event, wrapper);\n  }\n\n  protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n    const set = this.listeners.get(event);\n    if (!set) {\n      return;\n    }\n    for (const listener of [...set]) {\n      try {\n        listener(...args);\n      } catch (error) {\n        // eslint-disable-next-line no-console\n        console.error(\"[fish-agent] listener error\", error);\n      }\n    }\n  }\n}\n","import type { AgentSessionMessage, ClientSessionMessage } from \"@fishaudio/agent-protocol\";\nimport { FishAgentError } from \"../errors.js\";\n\nexport type ClientToolHandler = (\n  params: Record<string, unknown>,\n  context: { callId: string; toolName: string },\n) => unknown | Promise<unknown>;\n\nexport const DEFAULT_CLIENT_TOOL_TIMEOUT_MS = 15_000;\n\n/**\n * Upper bound on the serialized `client_tool.result` message. The realtime\n * data channel rejects packets above 64 000 bytes; this leaves headroom for\n * the packet envelope. Larger results are replaced by an error result so the\n * agent hears back instead of waiting out its server-side timeout.\n */\nexport const MAX_CLIENT_TOOL_RESULT_BYTES = 60_000;\n\nconst textEncoder = new TextEncoder();\n\ntype ClientToolCall = Extract<AgentSessionMessage, { type: \"client_tool.call\" }>;\n\nexport class ClientToolDispatcher {\n  private readonly tools = new Map<string, ClientToolHandler>();\n\n  constructor(\n    tools: Record<string, ClientToolHandler> | undefined,\n    private readonly timeoutMs: number,\n    private readonly send: (message: ClientSessionMessage) => Promise<void>,\n    private readonly onError: (error: FishAgentError) => void,\n  ) {\n    for (const [name, handler] of Object.entries(tools ?? {})) {\n      this.tools.set(name, handler);\n    }\n  }\n\n  register(name: string, handler: ClientToolHandler): void {\n    this.tools.set(name, handler);\n  }\n\n  async dispatch(call: ClientToolCall): Promise<void> {\n    const handler = this.tools.get(call.toolName);\n    if (!handler) {\n      this.onError(\n        new FishAgentError(\"tool_failed\", `Client tool \"${call.toolName}\" is not registered`),\n      );\n      if (call.expectsResponse) {\n        await this.reply(call.callId, { isError: true, result: \"Tool is not registered\" });\n      }\n      return;\n    }\n\n    try {\n      const result = await this.withTimeout(\n        Promise.resolve(handler(call.params, { callId: call.callId, toolName: call.toolName })),\n        call.toolName,\n      );\n      if (call.expectsResponse) {\n        await this.reply(call.callId, result === undefined ? {} : { result });\n      }\n    } catch (error) {\n      this.onError(\n        error instanceof FishAgentError\n          ? error\n          : new FishAgentError(\"tool_failed\", `Client tool \"${call.toolName}\" threw`, {\n              cause: error,\n            }),\n      );\n      if (call.expectsResponse) {\n        await this.reply(call.callId, { isError: true, result: String(error) });\n      }\n    }\n  }\n\n  private async reply(\n    callId: string,\n    body: { result?: unknown; isError?: boolean },\n  ): Promise<void> {\n    const message: ClientSessionMessage = { type: \"client_tool.result\", callId, ...body };\n    const rejected = this.undeliverable(message);\n    if (rejected !== undefined) {\n      this.onError(new FishAgentError(\"tool_failed\", rejected));\n      await this.deliver({ type: \"client_tool.result\", callId, isError: true, result: rejected });\n      return;\n    }\n    try {\n      await this.send(message);\n    } catch (error) {\n      this.onError(\n        new FishAgentError(\"tool_failed\", \"Failed to deliver a client tool result\", {\n          cause: error,\n        }),\n      );\n      if (body.isError) {\n        return;\n      }\n      // The agent is still waiting on this call. Trade the lost result for a\n      // short error result so it can recover in conversation now, rather than\n      // after its server-side tool timeout.\n      await this.deliver({\n        type: \"client_tool.result\",\n        callId,\n        isError: true,\n        result: `Client tool result could not be delivered: ${String(error)}`,\n      });\n    }\n  }\n\n  /** Why the message cannot go over the wire as-is, or undefined if it can. */\n  private undeliverable(message: ClientSessionMessage): string | undefined {\n    let serialized: string;\n    try {\n      serialized = JSON.stringify(message);\n    } catch (error) {\n      return `Client tool result is not JSON-serializable: ${String(error)}`;\n    }\n    const bytes = textEncoder.encode(serialized).byteLength;\n    if (bytes > MAX_CLIENT_TOOL_RESULT_BYTES) {\n      return (\n        `Client tool result is too large to send (${bytes} bytes serialized; ` +\n        `limit ${MAX_CLIENT_TOOL_RESULT_BYTES}). Return a summary or a reference instead.`\n      );\n    }\n    return undefined;\n  }\n\n  /** Last-resort send of an error result; a failure here is reported, not retried. */\n  private async deliver(message: ClientSessionMessage): Promise<void> {\n    try {\n      await this.send(message);\n    } catch (error) {\n      this.onError(\n        new FishAgentError(\"tool_failed\", \"Failed to deliver a client tool error result\", {\n          cause: error,\n        }),\n      );\n    }\n  }\n\n  private withTimeout<T>(promise: Promise<T>, toolName: string): Promise<T> {\n    return new Promise<T>((resolve, reject) => {\n      const timer = setTimeout(() => {\n        reject(\n          new FishAgentError(\n            \"tool_timeout\",\n            `Client tool \"${toolName}\" timed out after ${this.timeoutMs}ms`,\n          ),\n        );\n      }, this.timeoutMs);\n      promise.then(\n        (value) => {\n          clearTimeout(timer);\n          resolve(value);\n        },\n        (error: unknown) => {\n          clearTimeout(timer);\n          reject(error instanceof Error ? error : new Error(String(error)));\n        },\n      );\n    });\n  }\n}\n","/**\n * Holds a screen wake lock while a session is live (browser only; inert\n * elsewhere). The platform releases the lock whenever the page is hidden, so\n * visibility returns re-request it. Failures are silent by design: a session\n * works fine without the lock, the screen just may sleep mid-call.\n */\nexport class WakeLockHolder {\n  #sentinel?: WakeLockSentinel;\n  #active = false;\n\n  readonly #onVisibilityChange = (): void => {\n    if (document.visibilityState === \"visible\") {\n      void this.#request();\n    }\n  };\n\n  acquire(): void {\n    if (\n      this.#active ||\n      typeof document === \"undefined\" ||\n      typeof navigator === \"undefined\" ||\n      !navigator.wakeLock\n    ) {\n      return;\n    }\n    this.#active = true;\n    document.addEventListener(\"visibilitychange\", this.#onVisibilityChange);\n    void this.#request();\n  }\n\n  async #request(): Promise<void> {\n    if (!this.#active) {\n      return;\n    }\n    try {\n      const sentinel = await navigator.wakeLock.request(\"screen\");\n      if (!this.#active) {\n        void sentinel.release().catch(() => undefined);\n        return;\n      }\n      // Concurrent requests (acquire + visibility return) may both resolve;\n      // drop the earlier sentinel so exactly one is held.\n      const previous = this.#sentinel;\n      this.#sentinel = sentinel;\n      if (previous && previous !== sentinel) {\n        void previous.release().catch(() => undefined);\n      }\n    } catch {\n      // Denied (battery saver, hidden tab, permissions policy) — run without it.\n    }\n  }\n\n  release(): void {\n    if (!this.#active) {\n      return;\n    }\n    this.#active = false;\n    document.removeEventListener(\"visibilitychange\", this.#onVisibilityChange);\n    void this.#sentinel?.release().catch(() => undefined);\n    this.#sentinel = undefined;\n  }\n}\n"],"mappings":"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","names":["import_agent_protocol","LiveKitTransport","_a","_b"]}