fix: wait for a participant to become active, not just connected - #2403
Draft
tinalenguyen wants to merge 1 commit into
Draft
fix: wait for a participant to become active, not just connected#2403tinalenguyen wants to merge 1 commit into
tinalenguyen wants to merge 1 commit into
Conversation
`waitForParticipant` resolved on `RoomEvent.ParticipantConnected`, which fires while the participant is still JOINING/JOINED. A remote participant can only receive data messages once it reaches `ParticipantState.ACTIVE`, so callers got back a participant that is present in `room.remoteParticipants` but not yet reachable, and anything sent to it was silently dropped. That is most visible in `DataStreamAudioOutput`, which waits on this before streaming audio to an avatar worker — the wait could return early and publish into a void. Port the Python SDK's semantics, which have always waited on `participant_active`: resolve on `RoomEvent.ParticipantActive`, and when scanning participants that are already in the room require `state === ParticipantState.ACTIVE`. The local participant (`includeLocal`) is unaffected — it has no remote lifecycle to wait on. Depends on the matching @livekit/rtc-node change surfacing `RoomEvent.ParticipantActive` and `Participant.state`; this cannot merge until that ships.
🦋 Changeset detectedLatest commit: e68a8dd The changes in this PR will be included in the next version bump. This PR includes changesets to release 39 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
waitForParticipantresolves onRoomEvent.ParticipantConnected, which fires while the participant is stillJOINING/JOINED. But a remote participant can only receive data messages once it reachesParticipantState.ACTIVE. So callers get back a participant that is present inroom.remoteParticipantsyet not reachable, and anything sent to it is silently dropped.The most visible case is
DataStreamAudioOutput(agents/src/voice/avatar/datastream_io.ts:129), which waits on this before streaming audio to an avatar worker — the wait can return early and publish into a void.Python has always waited on
participant_activehere (utils/participant.py): it listens on that event and requiresp.state == PARTICIPANT_STATE_ACTIVEwhen scanning participants already in the room. JS had no equivalent because the Node SDK never surfaced the event — hence the companion PR.Changes
agents/src/utils.ts—waitForParticipantnow:RoomEvent.ParticipantActiveinstead ofParticipantConnectedstate === ParticipantState.ACTIVEwhen scanningroom.remoteParticipantsincludeLocalis deliberately untouched, matching Python: the local participant has no remote lifecycle to wait on.Every caller benefits —
ctx.waitForParticipant,DataStreamAudioOutput, andAvatarSession.Tests
Eight new tests in
agents/src/wait_for_participant.test.ts: immediate return for an already-active participant, continued waiting for connected-but-not-active (the actual bug), resolution via the event for a late joiner, identity filtering, kind filtering, disconnect rejection, abort rejection, and the local-participant path.Full suite passes — 2330 tests across 143 files. Lint and prettier clean.
pnpm api:checkfails, but identically on unmodifiedmain—etc/agents.api.mdis stale (interruptionDetectionordering,_redactionEnabled,RimeModels,_holdInterruptions). Verified by stashing and re-running. I left that drift alone rather than sweeping it into this PR; there is no participant-related API change.Merge order
@livekit/rtc-nodeinpnpm-workspace.yamlNote on the bey retry loop
This does not on its own fix the symptom that #2340 patches with a retry loop. That loop's log blames transient disconnect/reconnect, but in rtc-node
RoomEvent.Disconnectedis terminal — reconnects go throughReconnecting/Reconnected. The likelier trigger there iswaitForParticipant's synchronousthrow new Error('Room is not connected')during a reconnect window, which this PR doesn't touch. What this fixes is the quieter bug underneath: audio published to a connected-but-not-yet-active avatar. Worth re-testing the bey case against this to see whether the plugin-level retry is still needed.