{"version":3,"sources":["../../../src/voice/room_io/room_io.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { TextStreamReader } from '@livekit/rtc-node';\nimport {\n  type AudioFrame,\n  ConnectionState,\n  DisconnectReason,\n  type FrameProcessor,\n  type NoiseCancellationOptions,\n  type Participant,\n  ParticipantKind,\n  type RemoteParticipant,\n  type Room,\n  RoomEvent,\n  TrackPublishOptions,\n  TrackSource,\n} from '@livekit/rtc-node';\nimport type { WritableStreamDefaultWriter } from 'node:stream/web';\nimport { ATTRIBUTE_PUBLISH_ON_BEHALF, TOPIC_CHAT } from '../../constants.js';\nimport { type JobContext, getJobContext } from '../../job.js';\nimport { RealtimeModel } from '../../llm/index.js';\nimport { log } from '../../log.js';\nimport { IdentityTransform } from '../../stream/identity_transform.js';\nimport { DEFAULT_API_CONNECT_OPTIONS } from '../../types.js';\nimport { Future, IdleTimeoutError, Task, waitForAbort, waitUntilTimeout } from '../../utils.js';\nimport { type AgentSession } from '../agent_session.js';\nimport {\n  AgentSessionEventTypes,\n  type AgentStateChangedEvent,\n  CloseReason,\n  type ConversationItemAddedEvent,\n  type UserInputTranscribedEvent,\n} from '../events.js';\nimport type { AudioOutput, TextOutput } from '../io.js';\nimport type { TextInputCallback } from '../remote_session.js';\nimport {\n  TranscriptionSynchronizer,\n  defaultTextSyncOptions,\n} from '../transcription/synchronizer.js';\nimport { ParticipantAudioInputStream } from './_input.js';\nimport {\n  ParalellTextOutput,\n  ParticipantAudioOutput,\n  ParticipantLegacyTranscriptionOutput,\n  ParticipantTranscriptionOutput,\n} from './_output.js';\n\nexport const DEFAULT_TEXT_INPUT_CALLBACK: TextInputCallback = (sess, ev) => {\n  sess.interrupt();\n  sess.generateReply({ userInput: ev.text });\n};\n\nexport const DEFAULT_PARTICIPANT_KINDS: ParticipantKind[] = [\n  ParticipantKind.CONNECTOR,\n  ParticipantKind.SIP,\n  ParticipantKind.STANDARD,\n];\n\nconst CLOSE_ON_DISCONNECT_REASONS: DisconnectReason[] = [\n  DisconnectReason.CLIENT_INITIATED,\n  DisconnectReason.ROOM_DELETED,\n  DisconnectReason.USER_REJECTED,\n];\n\nexport interface RoomInputOptions {\n  audioSampleRate: number;\n  audioNumChannels: number;\n  /** If not given, default to True. */\n  textEnabled: boolean;\n  /** If not given, default to True. */\n  audioEnabled: boolean;\n  /** If not given, default to False. */\n  videoEnabled: boolean;\n  /** The participant to link to. If not provided, link to the first participant.\n    Can be overridden by the `participant` argument of RoomIO constructor or `set_participant`.\n  */\n  participantIdentity?: string;\n  noiseCancellation?: NoiseCancellationOptions | FrameProcessor<AudioFrame>;\n  textInputCallback?: TextInputCallback;\n  /** Participant kinds accepted for auto subscription. If not provided,\n    accept `DEFAULT_PARTICIPANT_KINDS`\n  */\n  participantKinds?: ParticipantKind[];\n  /** Close the AgentSession if the linked participant disconnects with reasons in\n    CLIENT_INITIATED, ROOM_DELETED, or USER_REJECTED.\n  */\n  closeOnDisconnect: boolean;\n  /** Delete the room when the AgentSession is closed. Default to false. */\n  deleteRoomOnClose: boolean;\n}\n\nexport interface RoomOutputOptions {\n  /** If not given, default to True. */\n  transcriptionEnabled: boolean;\n  /** If not given, default to True. */\n  audioEnabled: boolean;\n  audioSampleRate: number;\n  audioNumChannels: number;\n  /** False to disable transcription synchronization with audio output.\n    Otherwise, transcription is emitted as quickly as available.\n  */\n  syncTranscription: boolean;\n  /** The name of the audio track to publish. If not provided, default to \"roomio_audio\".\n   */\n  audioPublishOptions: TrackPublishOptions;\n  /** Maximum queue size in milliseconds for the audio output buffer.\n    When TTS generates audio faster than real-time, a larger queue prevents\n    early frames from being discarded by the ring buffer.\n    Defaults to 200ms, matching Python. Set a larger value if a bursty TTS\n    provider needs a larger prebuffer.\n  */\n  queueSizeMs?: number;\n  /** Send the transcription as a JSON dict for each chunk on the `lk.transcription`\n    datastream topic, including `start_time`/`end_time` timestamps if the chunk is a\n    TimedString. Each JSON object is suffixed with a newline so clients can parse the\n    stream line-by-line.\n  */\n  jsonFormat: boolean;\n}\n\nconst DEFAULT_ROOM_INPUT_OPTIONS: RoomInputOptions = {\n  audioSampleRate: 24000,\n  audioNumChannels: 1,\n  textEnabled: true,\n  audioEnabled: true,\n  videoEnabled: false,\n  textInputCallback: DEFAULT_TEXT_INPUT_CALLBACK,\n  closeOnDisconnect: true,\n  deleteRoomOnClose: false,\n};\n\nconst DEFAULT_ROOM_OUTPUT_OPTIONS: RoomOutputOptions = {\n  audioSampleRate: 24000,\n  audioNumChannels: 1,\n  transcriptionEnabled: true,\n  audioEnabled: true,\n  syncTranscription: true,\n  audioPublishOptions: new TrackPublishOptions({ source: TrackSource.SOURCE_MICROPHONE }),\n  queueSizeMs: 200,\n  jsonFormat: false,\n};\n\nexport class RoomIO {\n  private agentSession: AgentSession;\n  private room: Room;\n  private inputOptions: RoomInputOptions;\n  private outputOptions: RoomOutputOptions;\n\n  private audioInput?: ParticipantAudioInputStream;\n  private participantAudioOutput?: ParticipantAudioOutput;\n  private externalAudioOutput?: AudioOutput;\n  private userTranscriptOutput?: ParalellTextOutput;\n  private agentTranscriptOutput?: ParalellTextOutput;\n  private transcriptionSynchronizer?: TranscriptionSynchronizer;\n  private participantIdentity: string | null = null;\n  private textStreamHandlerRegistered = false;\n\n  private participantAvailableFuture: Future<RemoteParticipant> = new Future();\n  private roomConnectedFuture: Future<void> = new Future();\n\n  // Use stream API for transcript queue\n  private userTranscriptStream = new IdentityTransform<UserInputTranscribedEvent>();\n  private userTranscriptWriter: WritableStreamDefaultWriter<UserInputTranscribedEvent>;\n  private forwardUserTranscriptTask?: Task<void>;\n  private initTask?: Task<void>;\n  private deleteRoomTask?: Task<void>;\n  private jobContext?: JobContext;\n\n  private logger = log();\n\n  constructor({\n    agentSession,\n    room,\n    participant = null,\n    inputOptions,\n    outputOptions,\n  }: {\n    agentSession: AgentSession;\n    room: Room;\n    participant?: RemoteParticipant | string | null;\n    inputOptions?: Partial<RoomInputOptions>;\n    outputOptions?: Partial<RoomOutputOptions>;\n  }) {\n    this.agentSession = agentSession;\n    this.room = room;\n    this.inputOptions = { ...DEFAULT_ROOM_INPUT_OPTIONS, ...inputOptions };\n    this.outputOptions = { ...DEFAULT_ROOM_OUTPUT_OPTIONS, ...outputOptions };\n    this.userTranscriptWriter = this.userTranscriptStream.writable.getWriter();\n    this.jobContext = getJobContext(false);\n\n    this.participantIdentity = participant\n      ? typeof participant === 'string'\n        ? participant\n        : participant.identity\n      : this.inputOptions.participantIdentity ?? null;\n  }\n  private async init(signal: AbortSignal): Promise<void> {\n    await Promise.race([this.roomConnectedFuture.await, waitForAbort(signal)]);\n    if (signal.aborted) {\n      return;\n    }\n\n    for (const participant of this.room.remoteParticipants.values()) {\n      this.onParticipantConnected(participant);\n    }\n    if (signal.aborted) {\n      return;\n    }\n\n    const participant = await Promise.race([\n      this.participantAvailableFuture.await,\n      waitForAbort(signal),\n    ]);\n\n    if (!participant) {\n      return;\n    }\n\n    this.setParticipant(participant.identity);\n\n    // init agent outputs\n    this.updateTranscriptionOutput({\n      output: this.agentTranscriptOutput,\n      participant: this.room.localParticipant?.identity ?? null,\n    });\n\n    await this.participantAudioOutput?.start(signal);\n  }\n\n  private onConnectionStateChanged = (state: ConnectionState) => {\n    this.logger.debug({ state }, 'connection state changed');\n    if (\n      state === ConnectionState.CONN_CONNECTED &&\n      this.room.isConnected &&\n      !this.roomConnectedFuture.done\n    ) {\n      this.roomConnectedFuture.resolve();\n    }\n  };\n\n  private onParticipantConnected = (participant: RemoteParticipant) => {\n    if (this.participantAvailableFuture.done) {\n      return;\n    }\n\n    if (this.participantIdentity) {\n      if (participant.identity !== this.participantIdentity) {\n        return;\n      }\n    } else if (\n      // otherwise, skip participants that are marked as publishing for this agent\n      participant.attributes?.[ATTRIBUTE_PUBLISH_ON_BEHALF] === this.room.localParticipant?.identity\n    ) {\n      return;\n    }\n\n    const acceptedKinds = this.inputOptions.participantKinds ?? DEFAULT_PARTICIPANT_KINDS;\n    if (participant.info.kind !== undefined && !acceptedKinds.includes(participant.info.kind)) {\n      return;\n    }\n\n    this.participantAvailableFuture.resolve(participant);\n    this.agentSession._onRoomIOParticipantLinked(participant);\n  };\n\n  private onParticipantDisconnected = (participant: RemoteParticipant) => {\n    if (participant.identity !== this.participantIdentity) {\n      return;\n    }\n    this.participantAvailableFuture = new Future<RemoteParticipant>();\n    if (\n      this.inputOptions.closeOnDisconnect &&\n      participant.disconnectReason &&\n      CLOSE_ON_DISCONNECT_REASONS.includes(participant.disconnectReason)\n    ) {\n      this.logger.info(\n        {\n          'lk.pii.participant_identity': participant.identity,\n          reason: DisconnectReason[participant.disconnectReason],\n        },\n        'closing agent session due to participant disconnect ' +\n          '(disable via `RoomInputOptions.closeOnDisconnect=False`)',\n      );\n      this.agentSession._closeSoon({\n        reason: CloseReason.PARTICIPANT_DISCONNECTED,\n      });\n    }\n  };\n\n  private onUserInputTranscribed = (ev: UserInputTranscribedEvent) => {\n    this.userTranscriptWriter.write(ev).catch((error) => {\n      this.logger.error({ error }, 'Failed to write transcript event to stream');\n    });\n  };\n\n  private onConversationItemAdded = (ev: ConversationItemAddedEvent) => {\n    if (ev.item.type !== 'agent_handoff' || !this.transcriptionSynchronizer) {\n      return;\n    }\n    const sessionLlm = this.agentSession.currentAgent?.llm ?? this.agentSession.llm;\n    const nativeTranscriptSync =\n      sessionLlm instanceof RealtimeModel && !!sessionLlm.capabilities.nativeTranscriptSync;\n    this.transcriptionSynchronizer.enabled = !nativeTranscriptSync;\n  };\n\n  private onAgentSessionClose = () => {\n    if (!this.inputOptions.deleteRoomOnClose || this.deleteRoomTask) {\n      return;\n    }\n\n    let jobContext = this.jobContext;\n    if (!jobContext) {\n      jobContext = getJobContext(false);\n    }\n    if (!jobContext) {\n      return;\n    }\n    this.logger.info(\n      { 'lk.pii.room_name': this.room.name },\n      'deleting room on agent session close ' +\n        '(disable via `RoomInputOptions.deleteRoomOnClose=false`)',\n    );\n\n    const deleteRoomTask = Task.from(async () => {\n      await jobContext.deleteRoom(this.room.name);\n    });\n\n    this.deleteRoomTask = deleteRoomTask;\n    deleteRoomTask.addDoneCallback(() => {\n      if (this.deleteRoomTask === deleteRoomTask) {\n        this.deleteRoomTask = undefined;\n      }\n    });\n  };\n\n  private onAgentStateChanged = (ev: AgentStateChangedEvent) => {\n    if (this.room.isConnected && this.room.localParticipant) {\n      this.room.localParticipant\n        .setAttributes({\n          [`lk.agent.state`]: ev.newState,\n        })\n        .catch((error) => {\n          this.logger.error(error, 'Failed to update agent state attributes');\n        });\n    }\n  };\n\n  private onUserTextInput = (reader: TextStreamReader, participantInfo: { identity: string }) => {\n    if (this.participantIdentity && participantInfo.identity !== this.participantIdentity) {\n      return;\n    }\n\n    const participant = this.room.remoteParticipants.get(participantInfo.identity);\n    if (!participant) {\n      this.logger.warn('participant not found, ignoring text input');\n      return;\n    }\n\n    const readText = async () => {\n      const text = await reader.readAll();\n\n      const textInputResult = this.inputOptions.textInputCallback!(this.agentSession, {\n        text,\n        info: reader.info,\n        participantIdentity: participantInfo.identity,\n      });\n\n      // check if callback is a Promise\n      if (textInputResult instanceof Promise) {\n        await textInputResult;\n      }\n    };\n\n    readText().catch((error) => {\n      this.logger.error({ error }, 'Error reading text input');\n    });\n  };\n\n  private async forwardUserTranscript(signal: AbortSignal): Promise<void> {\n    const reader = this.userTranscriptStream.readable.getReader();\n    try {\n      while (!signal.aborted) {\n        const { done, value } = await reader.read();\n        if (done) break;\n\n        const event = value;\n        // IMPORTANT: need to await here to avoid race condition\n        await this.userTranscriptOutput?.captureText(event.transcript);\n        if (event.isFinal) {\n          this.userTranscriptOutput?.flush();\n        }\n      }\n    } catch (error) {\n      this.logger.error({ error }, 'Error processing transcript stream');\n    }\n  }\n\n  private createTranscriptionOutput(options: {\n    isDeltaStream: boolean;\n    participant: Participant | string | null;\n  }) {\n    // markup only ever reaches these sinks once expressive has injected its guide; until\n    // then they publish the agent's text verbatim\n    const expressiveEnabled = () => this.agentSession._expressiveEverActive;\n    return new ParalellTextOutput([\n      new ParticipantLegacyTranscriptionOutput(\n        this.room,\n        options.isDeltaStream,\n        options.participant,\n        { expressiveEnabled },\n      ),\n      new ParticipantTranscriptionOutput(this.room, options.isDeltaStream, options.participant, {\n        jsonFormat: this.outputOptions.jsonFormat,\n        expressiveEnabled,\n      }),\n    ]);\n  }\n\n  private updateTranscriptionOutput({\n    output,\n    participant,\n  }: {\n    output?: ParalellTextOutput;\n    participant: string | null;\n  }) {\n    if (!output) {\n      return;\n    }\n\n    for (const sink of output._sinks) {\n      if (\n        sink instanceof ParticipantLegacyTranscriptionOutput ||\n        sink instanceof ParticipantTranscriptionOutput\n      ) {\n        sink.setParticipant(participant);\n      }\n    }\n  }\n\n  get audioOutput(): AudioOutput | undefined {\n    if (!this.transcriptionSynchronizer) {\n      return this.participantAudioOutput ?? this.externalAudioOutput;\n    }\n\n    return this.transcriptionSynchronizer.audioOutput;\n  }\n\n  get transcriptionOutput(): TextOutput | undefined {\n    if (!this.transcriptionSynchronizer) {\n      return this.agentTranscriptOutput;\n    }\n\n    return this.transcriptionSynchronizer.textOutput;\n  }\n\n  get isParticipantAvailable(): boolean {\n    return this.participantAvailableFuture.done;\n  }\n\n  get rtcRoom(): Room {\n    return this.room;\n  }\n\n  get linkedParticipant(): RemoteParticipant | undefined {\n    if (!this.isParticipantAvailable) {\n      return undefined;\n    }\n\n    return this.participantAvailableFuture.result;\n  }\n\n  get localParticipant(): Participant | undefined {\n    return this.room.localParticipant ?? undefined;\n  }\n\n  /** Switch to a different participant */\n  setParticipant(participantIdentity: string | null) {\n    this.logger.debug(\n      { 'lk.pii.participant_identity': participantIdentity },\n      'setting participant',\n    );\n    if (participantIdentity === null) {\n      this.unsetParticipant();\n      return;\n    }\n\n    if (this.participantIdentity !== participantIdentity) {\n      this.participantAvailableFuture = new Future<RemoteParticipant>();\n\n      // check if new participant is already connected\n      for (const participant of this.room.remoteParticipants.values()) {\n        if (participant.identity === participantIdentity) {\n          this.participantAvailableFuture.resolve(participant);\n          break;\n        }\n      }\n    }\n\n    // update participant identity and handlers\n    this.participantIdentity = participantIdentity;\n    this.audioInput?.setParticipant(participantIdentity);\n    this.updateTranscriptionOutput({\n      output: this.userTranscriptOutput,\n      participant: participantIdentity,\n    });\n  }\n\n  unsetParticipant() {\n    this.participantIdentity = null;\n    this.participantAvailableFuture = new Future<RemoteParticipant>();\n    this.audioInput?.setParticipant(null);\n    this.updateTranscriptionOutput({\n      output: this.userTranscriptOutput,\n      participant: null,\n    });\n  }\n\n  start() {\n    // -- create inputs --\n\n    if (this.inputOptions.textEnabled) {\n      try {\n        this.room.registerTextStreamHandler(TOPIC_CHAT, this.onUserTextInput);\n        this.textStreamHandlerRegistered = true;\n      } catch (error) {\n        if (this.inputOptions.textEnabled) {\n          this.logger.warn(`text stream handler for topic \"${TOPIC_CHAT}\" already set, ignoring`);\n        }\n      }\n    }\n\n    // if the user pre-set an input, keep it instead of creating (and later attaching) our own\n    const externalAudioInput = this.agentSession.input.audio ?? undefined;\n    if (this.inputOptions.audioEnabled && !externalAudioInput) {\n      this.audioInput = new ParticipantAudioInputStream({\n        room: this.room,\n        sampleRate: this.inputOptions.audioSampleRate,\n        numChannels: this.inputOptions.audioNumChannels,\n        noiseCancellation: this.inputOptions.noiseCancellation,\n      });\n    }\n\n    // -- create outputs --\n    this.externalAudioOutput = this.agentSession.output.audio ?? undefined;\n    if (this.outputOptions.audioEnabled && !this.externalAudioOutput) {\n      this.participantAudioOutput = new ParticipantAudioOutput(this.room, {\n        sampleRate: this.outputOptions.audioSampleRate,\n        numChannels: this.outputOptions.audioNumChannels,\n        trackPublishOptions: this.outputOptions.audioPublishOptions,\n        queueSizeMs: this.outputOptions.queueSizeMs,\n      });\n    }\n    // if the user pre-set a transcription output, keep it: skip the room transcription\n    // outputs and the transcript synchronizer entirely (matches the python implementation,\n    // where a pre-set output disables RoomIO's text output)\n    const externalTranscriptionOutput = this.agentSession.output.transcription ?? undefined;\n    if (this.outputOptions.transcriptionEnabled && !externalTranscriptionOutput) {\n      this.userTranscriptOutput = this.createTranscriptionOutput({\n        isDeltaStream: false,\n        participant: this.participantIdentity,\n      });\n      // Start the transcript forwarding\n      this.forwardUserTranscriptTask = Task.from((controller) =>\n        this.forwardUserTranscript(controller.signal),\n      );\n      this.agentTranscriptOutput = this.createTranscriptionOutput({\n        isDeltaStream: true,\n        participant: null,\n      });\n\n      const audioOutput = this.participantAudioOutput ?? this.externalAudioOutput;\n      if (this.outputOptions.syncTranscription && audioOutput) {\n        const sessionLlm = this.agentSession.currentAgent?.llm ?? this.agentSession.llm;\n        const nativeTranscriptSync =\n          sessionLlm instanceof RealtimeModel && !!sessionLlm.capabilities.nativeTranscriptSync;\n        this.transcriptionSynchronizer = new TranscriptionSynchronizer(\n          audioOutput,\n          this.agentTranscriptOutput,\n          {\n            ...defaultTextSyncOptions,\n            enabled: !nativeTranscriptSync,\n            expressiveEnabled: () => this.agentSession._expressiveEverActive,\n          },\n        );\n      }\n    }\n\n    // -- set the room event handlers --\n    this.room.on(RoomEvent.ParticipantConnected, this.onParticipantConnected);\n    this.room.on(RoomEvent.ConnectionStateChanged, this.onConnectionStateChanged);\n    this.room.on(RoomEvent.ParticipantDisconnected, this.onParticipantDisconnected);\n    if (this.room.isConnected) {\n      this.onConnectionStateChanged(ConnectionState.CONN_CONNECTED);\n    }\n\n    this.initTask = Task.from((controller) => this.init(controller.signal));\n\n    // attach the agent to the session\n    if (this.audioInput) {\n      this.agentSession.input.audio = this.audioInput;\n    }\n    if (this.audioOutput) {\n      this.agentSession.output.audio = this.audioOutput;\n    }\n    if (this.transcriptionOutput) {\n      this.agentSession.output.transcription = this.transcriptionOutput;\n    }\n\n    this.agentSession.on(AgentSessionEventTypes.AgentStateChanged, this.onAgentStateChanged);\n    this.agentSession.on(AgentSessionEventTypes.UserInputTranscribed, this.onUserInputTranscribed);\n    this.agentSession.on(AgentSessionEventTypes.Close, this.onAgentSessionClose);\n    this.agentSession.on(\n      AgentSessionEventTypes.ConversationItemAdded,\n      this.onConversationItemAdded,\n    );\n  }\n\n  async close() {\n    this.room.off(RoomEvent.ParticipantConnected, this.onParticipantConnected);\n    this.room.off(RoomEvent.ConnectionStateChanged, this.onConnectionStateChanged);\n    this.room.off(RoomEvent.ParticipantDisconnected, this.onParticipantDisconnected);\n    this.agentSession.off(AgentSessionEventTypes.UserInputTranscribed, this.onUserInputTranscribed);\n    this.agentSession.off(AgentSessionEventTypes.AgentStateChanged, this.onAgentStateChanged);\n    this.agentSession.off(AgentSessionEventTypes.Close, this.onAgentSessionClose);\n    this.agentSession.off(\n      AgentSessionEventTypes.ConversationItemAdded,\n      this.onConversationItemAdded,\n    );\n\n    if (this.textStreamHandlerRegistered) {\n      this.room.unregisterTextStreamHandler(TOPIC_CHAT);\n      this.textStreamHandlerRegistered = false;\n    }\n\n    await this.initTask?.cancelAndWait();\n\n    // Close stream FIRST so reader.read() in forwardUserTranscript can exit.\n    // Writer may already be closed or errored if a concurrent write failed\n    // during session teardown (e.g. speech interruption race). Safe to ignore.\n    try {\n      await this.userTranscriptWriter.close();\n    } catch (e) {\n      this.logger.debug({ error: e }, 'userTranscriptWriter already closed or errored');\n    }\n    await this.forwardUserTranscriptTask?.cancelAndWait();\n\n    await this.audioInput?.close();\n    await this.participantAudioOutput?.close();\n    await this.transcriptionSynchronizer?.close();\n\n    if (this.deleteRoomTask) {\n      try {\n        await waitUntilTimeout(this.deleteRoomTask.result, DEFAULT_API_CONNECT_OPTIONS.timeoutMs);\n      } catch (error) {\n        if (!(error instanceof IdleTimeoutError)) {\n          throw error;\n        }\n        this.logger.warn(\n          { 'lk.pii.room_name': this.room.name },\n          'automatic room deletion timed out',\n        );\n      }\n    }\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAaO;AAEP,uBAAwD;AACxD,iBAA+C;AAC/C,iBAA8B;AAC9B,iBAAoB;AACpB,gCAAkC;AAClC,mBAA4C;AAC5C,mBAA+E;AAC/E,2BAAkC;AAClC,oBAMO;AAGP,0BAGO;AACP,mBAA4C;AAC5C,oBAKO;AAEA,MAAM,8BAAiD,CAAC,MAAM,OAAO;AAC1E,OAAK,UAAU;AACf,OAAK,cAAc,EAAE,WAAW,GAAG,KAAK,CAAC;AAC3C;AAEO,MAAM,4BAA+C;AAAA,EAC1D,gCAAgB;AAAA,EAChB,gCAAgB;AAAA,EAChB,gCAAgB;AAClB;AAEA,MAAM,8BAAkD;AAAA,EACtD,iCAAiB;AAAA,EACjB,iCAAiB;AAAA,EACjB,iCAAiB;AACnB;AA0DA,MAAM,6BAA+C;AAAA,EACnD,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,mBAAmB;AACrB;AAEA,MAAM,8BAAiD;AAAA,EACrD,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,qBAAqB,IAAI,oCAAoB,EAAE,QAAQ,4BAAY,kBAAkB,CAAC;AAAA,EACtF,aAAa;AAAA,EACb,YAAY;AACd;AAEO,MAAM,OAAO;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAqC;AAAA,EACrC,8BAA8B;AAAA,EAE9B,6BAAwD,IAAI,oBAAO;AAAA,EACnE,sBAAoC,IAAI,oBAAO;AAAA;AAAA,EAG/C,uBAAuB,IAAI,4CAA6C;AAAA,EACxE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,aAAS,gBAAI;AAAA,EAErB,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,GAMG;AACD,SAAK,eAAe;AACpB,SAAK,OAAO;AACZ,SAAK,eAAe,EAAE,GAAG,4BAA4B,GAAG,aAAa;AACrE,SAAK,gBAAgB,EAAE,GAAG,6BAA6B,GAAG,cAAc;AACxE,SAAK,uBAAuB,KAAK,qBAAqB,SAAS,UAAU;AACzE,SAAK,iBAAa,0BAAc,KAAK;AAErC,SAAK,sBAAsB,cACvB,OAAO,gBAAgB,WACrB,cACA,YAAY,WACd,KAAK,aAAa,uBAAuB;AAAA,EAC/C;AAAA,EACA,MAAc,KAAK,QAAoC;AArMzD;AAsMI,UAAM,QAAQ,KAAK,CAAC,KAAK,oBAAoB,WAAO,2BAAa,MAAM,CAAC,CAAC;AACzE,QAAI,OAAO,SAAS;AAClB;AAAA,IACF;AAEA,eAAWA,gBAAe,KAAK,KAAK,mBAAmB,OAAO,GAAG;AAC/D,WAAK,uBAAuBA,YAAW;AAAA,IACzC;AACA,QAAI,OAAO,SAAS;AAClB;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,QAAQ,KAAK;AAAA,MACrC,KAAK,2BAA2B;AAAA,UAChC,2BAAa,MAAM;AAAA,IACrB,CAAC;AAED,QAAI,CAAC,aAAa;AAChB;AAAA,IACF;AAEA,SAAK,eAAe,YAAY,QAAQ;AAGxC,SAAK,0BAA0B;AAAA,MAC7B,QAAQ,KAAK;AAAA,MACb,eAAa,UAAK,KAAK,qBAAV,mBAA4B,aAAY;AAAA,IACvD,CAAC;AAED,YAAM,UAAK,2BAAL,mBAA6B,MAAM;AAAA,EAC3C;AAAA,EAEQ,2BAA2B,CAAC,UAA2B;AAC7D,SAAK,OAAO,MAAM,EAAE,MAAM,GAAG,0BAA0B;AACvD,QACE,UAAU,gCAAgB,kBAC1B,KAAK,KAAK,eACV,CAAC,KAAK,oBAAoB,MAC1B;AACA,WAAK,oBAAoB,QAAQ;AAAA,IACnC;AAAA,EACF;AAAA,EAEQ,yBAAyB,CAAC,gBAAmC;AAjPvE;AAkPI,QAAI,KAAK,2BAA2B,MAAM;AACxC;AAAA,IACF;AAEA,QAAI,KAAK,qBAAqB;AAC5B,UAAI,YAAY,aAAa,KAAK,qBAAqB;AACrD;AAAA,MACF;AAAA,IACF;AAAA;AAAA,QAEE,iBAAY,eAAZ,mBAAyB,qDAAiC,UAAK,KAAK,qBAAV,mBAA4B;AAAA,MACtF;AACA;AAAA,IACF;AAEA,UAAM,gBAAgB,KAAK,aAAa,oBAAoB;AAC5D,QAAI,YAAY,KAAK,SAAS,UAAa,CAAC,cAAc,SAAS,YAAY,KAAK,IAAI,GAAG;AACzF;AAAA,IACF;AAEA,SAAK,2BAA2B,QAAQ,WAAW;AACnD,SAAK,aAAa,2BAA2B,WAAW;AAAA,EAC1D;AAAA,EAEQ,4BAA4B,CAAC,gBAAmC;AACtE,QAAI,YAAY,aAAa,KAAK,qBAAqB;AACrD;AAAA,IACF;AACA,SAAK,6BAA6B,IAAI,oBAA0B;AAChE,QACE,KAAK,aAAa,qBAClB,YAAY,oBACZ,4BAA4B,SAAS,YAAY,gBAAgB,GACjE;AACA,WAAK,OAAO;AAAA,QACV;AAAA,UACE,+BAA+B,YAAY;AAAA,UAC3C,QAAQ,iCAAiB,YAAY,gBAAgB;AAAA,QACvD;AAAA,QACA;AAAA,MAEF;AACA,WAAK,aAAa,WAAW;AAAA,QAC3B,QAAQ,0BAAY;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,yBAAyB,CAAC,OAAkC;AAClE,SAAK,qBAAqB,MAAM,EAAE,EAAE,MAAM,CAAC,UAAU;AACnD,WAAK,OAAO,MAAM,EAAE,MAAM,GAAG,4CAA4C;AAAA,IAC3E,CAAC;AAAA,EACH;AAAA,EAEQ,0BAA0B,CAAC,OAAmC;AAxSxE;AAySI,QAAI,GAAG,KAAK,SAAS,mBAAmB,CAAC,KAAK,2BAA2B;AACvE;AAAA,IACF;AACA,UAAM,eAAa,UAAK,aAAa,iBAAlB,mBAAgC,QAAO,KAAK,aAAa;AAC5E,UAAM,uBACJ,sBAAsB,4BAAiB,CAAC,CAAC,WAAW,aAAa;AACnE,SAAK,0BAA0B,UAAU,CAAC;AAAA,EAC5C;AAAA,EAEQ,sBAAsB,MAAM;AAClC,QAAI,CAAC,KAAK,aAAa,qBAAqB,KAAK,gBAAgB;AAC/D;AAAA,IACF;AAEA,QAAI,aAAa,KAAK;AACtB,QAAI,CAAC,YAAY;AACf,uBAAa,0BAAc,KAAK;AAAA,IAClC;AACA,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AACA,SAAK,OAAO;AAAA,MACV,EAAE,oBAAoB,KAAK,KAAK,KAAK;AAAA,MACrC;AAAA,IAEF;AAEA,UAAM,iBAAiB,kBAAK,KAAK,YAAY;AAC3C,YAAM,WAAW,WAAW,KAAK,KAAK,IAAI;AAAA,IAC5C,CAAC;AAED,SAAK,iBAAiB;AACtB,mBAAe,gBAAgB,MAAM;AACnC,UAAI,KAAK,mBAAmB,gBAAgB;AAC1C,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,sBAAsB,CAAC,OAA+B;AAC5D,QAAI,KAAK,KAAK,eAAe,KAAK,KAAK,kBAAkB;AACvD,WAAK,KAAK,iBACP,cAAc;AAAA,QACb,CAAC,gBAAgB,GAAG,GAAG;AAAA,MACzB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,aAAK,OAAO,MAAM,OAAO,yCAAyC;AAAA,MACpE,CAAC;AAAA,IACL;AAAA,EACF;AAAA,EAEQ,kBAAkB,CAAC,QAA0B,oBAA0C;AAC7F,QAAI,KAAK,uBAAuB,gBAAgB,aAAa,KAAK,qBAAqB;AACrF;AAAA,IACF;AAEA,UAAM,cAAc,KAAK,KAAK,mBAAmB,IAAI,gBAAgB,QAAQ;AAC7E,QAAI,CAAC,aAAa;AAChB,WAAK,OAAO,KAAK,4CAA4C;AAC7D;AAAA,IACF;AAEA,UAAM,WAAW,YAAY;AAC3B,YAAM,OAAO,MAAM,OAAO,QAAQ;AAElC,YAAM,kBAAkB,KAAK,aAAa,kBAAmB,KAAK,cAAc;AAAA,QAC9E;AAAA,QACA,MAAM,OAAO;AAAA,QACb,qBAAqB,gBAAgB;AAAA,MACvC,CAAC;AAGD,UAAI,2BAA2B,SAAS;AACtC,cAAM;AAAA,MACR;AAAA,IACF;AAEA,aAAS,EAAE,MAAM,CAAC,UAAU;AAC1B,WAAK,OAAO,MAAM,EAAE,MAAM,GAAG,0BAA0B;AAAA,IACzD,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,sBAAsB,QAAoC;AA3X1E;AA4XI,UAAM,SAAS,KAAK,qBAAqB,SAAS,UAAU;AAC5D,QAAI;AACF,aAAO,CAAC,OAAO,SAAS;AACtB,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,cAAM,QAAQ;AAEd,gBAAM,UAAK,yBAAL,mBAA2B,YAAY,MAAM;AACnD,YAAI,MAAM,SAAS;AACjB,qBAAK,yBAAL,mBAA2B;AAAA,QAC7B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,EAAE,MAAM,GAAG,oCAAoC;AAAA,IACnE;AAAA,EACF;AAAA,EAEQ,0BAA0B,SAG/B;AAGD,UAAM,oBAAoB,MAAM,KAAK,aAAa;AAClD,WAAO,IAAI,iCAAmB;AAAA,MAC5B,IAAI;AAAA,QACF,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,EAAE,kBAAkB;AAAA,MACtB;AAAA,MACA,IAAI,6CAA+B,KAAK,MAAM,QAAQ,eAAe,QAAQ,aAAa;AAAA,QACxF,YAAY,KAAK,cAAc;AAAA,QAC/B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,0BAA0B;AAAA,IAChC;AAAA,IACA;AAAA,EACF,GAGG;AACD,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,eAAW,QAAQ,OAAO,QAAQ;AAChC,UACE,gBAAgB,sDAChB,gBAAgB,8CAChB;AACA,aAAK,eAAe,WAAW;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,cAAuC;AACzC,QAAI,CAAC,KAAK,2BAA2B;AACnC,aAAO,KAAK,0BAA0B,KAAK;AAAA,IAC7C;AAEA,WAAO,KAAK,0BAA0B;AAAA,EACxC;AAAA,EAEA,IAAI,sBAA8C;AAChD,QAAI,CAAC,KAAK,2BAA2B;AACnC,aAAO,KAAK;AAAA,IACd;AAEA,WAAO,KAAK,0BAA0B;AAAA,EACxC;AAAA,EAEA,IAAI,yBAAkC;AACpC,WAAO,KAAK,2BAA2B;AAAA,EACzC;AAAA,EAEA,IAAI,UAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,oBAAmD;AACrD,QAAI,CAAC,KAAK,wBAAwB;AAChC,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,2BAA2B;AAAA,EACzC;AAAA,EAEA,IAAI,mBAA4C;AAC9C,WAAO,KAAK,KAAK,oBAAoB;AAAA,EACvC;AAAA;AAAA,EAGA,eAAe,qBAAoC;AA7drD;AA8dI,SAAK,OAAO;AAAA,MACV,EAAE,+BAA+B,oBAAoB;AAAA,MACrD;AAAA,IACF;AACA,QAAI,wBAAwB,MAAM;AAChC,WAAK,iBAAiB;AACtB;AAAA,IACF;AAEA,QAAI,KAAK,wBAAwB,qBAAqB;AACpD,WAAK,6BAA6B,IAAI,oBAA0B;AAGhE,iBAAW,eAAe,KAAK,KAAK,mBAAmB,OAAO,GAAG;AAC/D,YAAI,YAAY,aAAa,qBAAqB;AAChD,eAAK,2BAA2B,QAAQ,WAAW;AACnD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,sBAAsB;AAC3B,eAAK,eAAL,mBAAiB,eAAe;AAChC,SAAK,0BAA0B;AAAA,MAC7B,QAAQ,KAAK;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,mBAAmB;AA5frB;AA6fI,SAAK,sBAAsB;AAC3B,SAAK,6BAA6B,IAAI,oBAA0B;AAChE,eAAK,eAAL,mBAAiB,eAAe;AAChC,SAAK,0BAA0B;AAAA,MAC7B,QAAQ,KAAK;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ;AAtgBV;AAygBI,QAAI,KAAK,aAAa,aAAa;AACjC,UAAI;AACF,aAAK,KAAK,0BAA0B,6BAAY,KAAK,eAAe;AACpE,aAAK,8BAA8B;AAAA,MACrC,SAAS,OAAO;AACd,YAAI,KAAK,aAAa,aAAa;AACjC,eAAK,OAAO,KAAK,kCAAkC,2BAAU,yBAAyB;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,qBAAqB,KAAK,aAAa,MAAM,SAAS;AAC5D,QAAI,KAAK,aAAa,gBAAgB,CAAC,oBAAoB;AACzD,WAAK,aAAa,IAAI,yCAA4B;AAAA,QAChD,MAAM,KAAK;AAAA,QACX,YAAY,KAAK,aAAa;AAAA,QAC9B,aAAa,KAAK,aAAa;AAAA,QAC/B,mBAAmB,KAAK,aAAa;AAAA,MACvC,CAAC;AAAA,IACH;AAGA,SAAK,sBAAsB,KAAK,aAAa,OAAO,SAAS;AAC7D,QAAI,KAAK,cAAc,gBAAgB,CAAC,KAAK,qBAAqB;AAChE,WAAK,yBAAyB,IAAI,qCAAuB,KAAK,MAAM;AAAA,QAClE,YAAY,KAAK,cAAc;AAAA,QAC/B,aAAa,KAAK,cAAc;AAAA,QAChC,qBAAqB,KAAK,cAAc;AAAA,QACxC,aAAa,KAAK,cAAc;AAAA,MAClC,CAAC;AAAA,IACH;AAIA,UAAM,8BAA8B,KAAK,aAAa,OAAO,iBAAiB;AAC9E,QAAI,KAAK,cAAc,wBAAwB,CAAC,6BAA6B;AAC3E,WAAK,uBAAuB,KAAK,0BAA0B;AAAA,QACzD,eAAe;AAAA,QACf,aAAa,KAAK;AAAA,MACpB,CAAC;AAED,WAAK,4BAA4B,kBAAK;AAAA,QAAK,CAAC,eAC1C,KAAK,sBAAsB,WAAW,MAAM;AAAA,MAC9C;AACA,WAAK,wBAAwB,KAAK,0BAA0B;AAAA,QAC1D,eAAe;AAAA,QACf,aAAa;AAAA,MACf,CAAC;AAED,YAAM,cAAc,KAAK,0BAA0B,KAAK;AACxD,UAAI,KAAK,cAAc,qBAAqB,aAAa;AACvD,cAAM,eAAa,UAAK,aAAa,iBAAlB,mBAAgC,QAAO,KAAK,aAAa;AAC5E,cAAM,uBACJ,sBAAsB,4BAAiB,CAAC,CAAC,WAAW,aAAa;AACnE,aAAK,4BAA4B,IAAI;AAAA,UACnC;AAAA,UACA,KAAK;AAAA,UACL;AAAA,YACE,GAAG;AAAA,YACH,SAAS,CAAC;AAAA,YACV,mBAAmB,MAAM,KAAK,aAAa;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,KAAK,GAAG,0BAAU,sBAAsB,KAAK,sBAAsB;AACxE,SAAK,KAAK,GAAG,0BAAU,wBAAwB,KAAK,wBAAwB;AAC5E,SAAK,KAAK,GAAG,0BAAU,yBAAyB,KAAK,yBAAyB;AAC9E,QAAI,KAAK,KAAK,aAAa;AACzB,WAAK,yBAAyB,gCAAgB,cAAc;AAAA,IAC9D;AAEA,SAAK,WAAW,kBAAK,KAAK,CAAC,eAAe,KAAK,KAAK,WAAW,MAAM,CAAC;AAGtE,QAAI,KAAK,YAAY;AACnB,WAAK,aAAa,MAAM,QAAQ,KAAK;AAAA,IACvC;AACA,QAAI,KAAK,aAAa;AACpB,WAAK,aAAa,OAAO,QAAQ,KAAK;AAAA,IACxC;AACA,QAAI,KAAK,qBAAqB;AAC5B,WAAK,aAAa,OAAO,gBAAgB,KAAK;AAAA,IAChD;AAEA,SAAK,aAAa,GAAG,qCAAuB,mBAAmB,KAAK,mBAAmB;AACvF,SAAK,aAAa,GAAG,qCAAuB,sBAAsB,KAAK,sBAAsB;AAC7F,SAAK,aAAa,GAAG,qCAAuB,OAAO,KAAK,mBAAmB;AAC3E,SAAK,aAAa;AAAA,MAChB,qCAAuB;AAAA,MACvB,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ;AA1mBhB;AA2mBI,SAAK,KAAK,IAAI,0BAAU,sBAAsB,KAAK,sBAAsB;AACzE,SAAK,KAAK,IAAI,0BAAU,wBAAwB,KAAK,wBAAwB;AAC7E,SAAK,KAAK,IAAI,0BAAU,yBAAyB,KAAK,yBAAyB;AAC/E,SAAK,aAAa,IAAI,qCAAuB,sBAAsB,KAAK,sBAAsB;AAC9F,SAAK,aAAa,IAAI,qCAAuB,mBAAmB,KAAK,mBAAmB;AACxF,SAAK,aAAa,IAAI,qCAAuB,OAAO,KAAK,mBAAmB;AAC5E,SAAK,aAAa;AAAA,MAChB,qCAAuB;AAAA,MACvB,KAAK;AAAA,IACP;AAEA,QAAI,KAAK,6BAA6B;AACpC,WAAK,KAAK,4BAA4B,2BAAU;AAChD,WAAK,8BAA8B;AAAA,IACrC;AAEA,YAAM,UAAK,aAAL,mBAAe;AAKrB,QAAI;AACF,YAAM,KAAK,qBAAqB,MAAM;AAAA,IACxC,SAAS,GAAG;AACV,WAAK,OAAO,MAAM,EAAE,OAAO,EAAE,GAAG,gDAAgD;AAAA,IAClF;AACA,YAAM,UAAK,8BAAL,mBAAgC;AAEtC,YAAM,UAAK,eAAL,mBAAiB;AACvB,YAAM,UAAK,2BAAL,mBAA6B;AACnC,YAAM,UAAK,8BAAL,mBAAgC;AAEtC,QAAI,KAAK,gBAAgB;AACvB,UAAI;AACF,kBAAM,+BAAiB,KAAK,eAAe,QAAQ,yCAA4B,SAAS;AAAA,MAC1F,SAAS,OAAO;AACd,YAAI,EAAE,iBAAiB,gCAAmB;AACxC,gBAAM;AAAA,QACR;AACA,aAAK,OAAO;AAAA,UACV,EAAE,oBAAoB,KAAK,KAAK,KAAK;AAAA,UACrC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["participant"]}