From 0a9c2c51a5f626b38d6f2d8666404dbfd2bc6ef4 Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Fri, 24 Oct 2025 12:52:37 +0200 Subject: [PATCH 1/5] feat: temporarily copy decodeTokenPayload into this project from livekit-client I should be able to get rid of this once this is merged: https://github.com/livekit/client-sdk-js/pull/1710 --- packages/react/etc/components-react.api.md | 4 ++++ packages/react/src/hooks/useSession.ts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/react/etc/components-react.api.md b/packages/react/etc/components-react.api.md index b69229c2f..23e8335c7 100644 --- a/packages/react/etc/components-react.api.md +++ b/packages/react/etc/components-react.api.md @@ -58,6 +58,7 @@ import { SourcesArray } from '@livekit/components-core'; import { SVGProps } from 'react'; import { TextStreamData } from '@livekit/components-core'; import { ToggleSource } from '@livekit/components-core'; +import { TokenPayload } from 'livekit-client'; import { TokenSourceConfigurable } from 'livekit-client'; import { TokenSourceFetchOptions } from 'livekit-client'; import { TokenSourceFixed } from 'livekit-client'; @@ -300,6 +301,9 @@ export interface ControlBarProps extends React_2.HTMLAttributes variation?: 'minimal' | 'verbose' | 'textOnly'; } +// @public +export function decodeTokenPayload(token: string): TokenPayload; + // @public export const DisconnectButton: (props: DisconnectButtonProps & React_2.RefAttributes) => React_2.ReactNode; diff --git a/packages/react/src/hooks/useSession.ts b/packages/react/src/hooks/useSession.ts index c79cb0791..04447c577 100644 --- a/packages/react/src/hooks/useSession.ts +++ b/packages/react/src/hooks/useSession.ts @@ -10,6 +10,8 @@ import { TokenSourceFixed, TokenSourceFetchOptions, RoomConnectOptions, + TokenPayload, + RoomConfigurationObject, } from 'livekit-client'; import { EventEmitter } from 'events'; @@ -17,6 +19,8 @@ import { useMaybeRoomContext } from '../context'; import { AgentState, useAgent, useAgentTimeoutIdStore } from './useAgent'; import { TrackReference } from '@livekit/components-core'; import { useLocalParticipant } from './useLocalParticipant'; +import { decodeJwt } from 'jose'; +import { RoomConfiguration } from '@livekit/protocol'; /** @public */ export enum SessionEvent { @@ -153,6 +157,22 @@ type UseSessionCommonOptions = { type UseSessionConfigurableOptions = UseSessionCommonOptions & TokenSourceFetchOptions; type UseSessionFixedOptions = UseSessionCommonOptions; +/** Given a LiveKit generated participant token, decodes and returns the associated {@link TokenPayload} data. */ +export function decodeTokenPayload(token: string) { + const payload = decodeJwt>(token); + + const { roomConfig, ...rest } = payload; + + const mappedPayload: TokenPayload = { + ...rest, + roomConfig: roomConfig + ? (RoomConfiguration.fromJson(roomConfig as Record) as RoomConfigurationObject) + : undefined, + }; + + return mappedPayload; +} + /** * A Session represents a manages connection to a Room which can contain Agents. * @public From c0bac8a24c42172996b5a7233cd5828d1f95a775 Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Fri, 24 Oct 2025 13:00:37 +0200 Subject: [PATCH 2/5] feat: add logic to only wait for agent to be available if an agent was dispatched --- packages/react/src/hooks/useSession.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/react/src/hooks/useSession.ts b/packages/react/src/hooks/useSession.ts index 04447c577..a5087945d 100644 --- a/packages/react/src/hooks/useSession.ts +++ b/packages/react/src/hooks/useSession.ts @@ -471,12 +471,18 @@ export function useSession( }; signal?.addEventListener('abort', onSignalAbort); + let tokenDispatchesAgent = false; await Promise.all([ // FIXME: swap the below line in once the new `livekit-client` changes are published // room.connect(tokenSource, { tokenSourceOptions }), - tokenSourceFetch().then(({ serverUrl, participantToken }) => - room.connect(serverUrl, participantToken, roomConnectOptions), - ), + tokenSourceFetch().then(({ serverUrl, participantToken }) => { + const participantTokenPayload = decodeTokenPayload(participantToken); + const participantTokenAgentDispatchCount = + participantTokenPayload.roomConfig?.agents?.length ?? 0; + tokenDispatchesAgent = participantTokenAgentDispatchCount > 0; + + return room.connect(serverUrl, participantToken, roomConnectOptions); + }), // Start microphone (with preconnect buffer) by default tracks.microphone?.enabled @@ -489,7 +495,9 @@ export function useSession( ]); await waitUntilConnected(signal); - await agent.waitUntilAvailable(signal); + if (tokenDispatchesAgent) { + await agent.waitUntilAvailable(signal); + } signal?.removeEventListener('abort', onSignalAbort); }, From 388ac32e4e3b7b8503af1abe78bf0bf2d297381c Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Fri, 24 Oct 2025 15:39:24 +0200 Subject: [PATCH 3/5] fix: add changeset --- .changeset/slick-cougars-pick.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slick-cougars-pick.md diff --git a/.changeset/slick-cougars-pick.md b/.changeset/slick-cougars-pick.md new file mode 100644 index 000000000..ce85ad5a0 --- /dev/null +++ b/.changeset/slick-cougars-pick.md @@ -0,0 +1,5 @@ +--- +'@livekit/components-react': patch +--- + +Make useSession wait for agent only if an agent was dispatched From 3ab537ff85a5fbb64bc679261426029979fa763f Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Tue, 28 Oct 2025 09:30:11 +0100 Subject: [PATCH 4/5] Revert "feat: temporarily copy decodeTokenPayload into this project from livekit-client" This reverts commit 0a9c2c51a5f626b38d6f2d8666404dbfd2bc6ef4. --- packages/react/etc/components-react.api.md | 4 ---- packages/react/src/hooks/useSession.ts | 20 -------------------- 2 files changed, 24 deletions(-) diff --git a/packages/react/etc/components-react.api.md b/packages/react/etc/components-react.api.md index 23e8335c7..b69229c2f 100644 --- a/packages/react/etc/components-react.api.md +++ b/packages/react/etc/components-react.api.md @@ -58,7 +58,6 @@ import { SourcesArray } from '@livekit/components-core'; import { SVGProps } from 'react'; import { TextStreamData } from '@livekit/components-core'; import { ToggleSource } from '@livekit/components-core'; -import { TokenPayload } from 'livekit-client'; import { TokenSourceConfigurable } from 'livekit-client'; import { TokenSourceFetchOptions } from 'livekit-client'; import { TokenSourceFixed } from 'livekit-client'; @@ -301,9 +300,6 @@ export interface ControlBarProps extends React_2.HTMLAttributes variation?: 'minimal' | 'verbose' | 'textOnly'; } -// @public -export function decodeTokenPayload(token: string): TokenPayload; - // @public export const DisconnectButton: (props: DisconnectButtonProps & React_2.RefAttributes) => React_2.ReactNode; diff --git a/packages/react/src/hooks/useSession.ts b/packages/react/src/hooks/useSession.ts index a5087945d..a8a64ede7 100644 --- a/packages/react/src/hooks/useSession.ts +++ b/packages/react/src/hooks/useSession.ts @@ -10,8 +10,6 @@ import { TokenSourceFixed, TokenSourceFetchOptions, RoomConnectOptions, - TokenPayload, - RoomConfigurationObject, } from 'livekit-client'; import { EventEmitter } from 'events'; @@ -19,8 +17,6 @@ import { useMaybeRoomContext } from '../context'; import { AgentState, useAgent, useAgentTimeoutIdStore } from './useAgent'; import { TrackReference } from '@livekit/components-core'; import { useLocalParticipant } from './useLocalParticipant'; -import { decodeJwt } from 'jose'; -import { RoomConfiguration } from '@livekit/protocol'; /** @public */ export enum SessionEvent { @@ -157,22 +153,6 @@ type UseSessionCommonOptions = { type UseSessionConfigurableOptions = UseSessionCommonOptions & TokenSourceFetchOptions; type UseSessionFixedOptions = UseSessionCommonOptions; -/** Given a LiveKit generated participant token, decodes and returns the associated {@link TokenPayload} data. */ -export function decodeTokenPayload(token: string) { - const payload = decodeJwt>(token); - - const { roomConfig, ...rest } = payload; - - const mappedPayload: TokenPayload = { - ...rest, - roomConfig: roomConfig - ? (RoomConfiguration.fromJson(roomConfig as Record) as RoomConfigurationObject) - : undefined, - }; - - return mappedPayload; -} - /** * A Session represents a manages connection to a Room which can contain Agents. * @public From d8cc4880edbf11c6c2d5de971d6d2b4bc3bdab73 Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Tue, 28 Oct 2025 09:35:04 +0100 Subject: [PATCH 5/5] feat: install updated livekit-client package with new decodeTokenPayload export --- packages/react/src/hooks/useSession.ts | 1 + pnpm-lock.yaml | 62 ++++++++++++++------------ pnpm-workspace.yaml | 2 +- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/packages/react/src/hooks/useSession.ts b/packages/react/src/hooks/useSession.ts index a8a64ede7..c3c66a7ef 100644 --- a/packages/react/src/hooks/useSession.ts +++ b/packages/react/src/hooks/useSession.ts @@ -10,6 +10,7 @@ import { TokenSourceFixed, TokenSourceFetchOptions, RoomConnectOptions, + decodeTokenPayload, } from 'livekit-client'; import { EventEmitter } from 'events'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7445f579a..0acbccb44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,9 @@ catalogs: '@livekit/protocol': specifier: ^1.42.0 version: 1.42.0 + livekit-client: + specifier: ^2.15.14 + version: 2.15.14 importers: @@ -64,7 +67,7 @@ importers: version: link:../../packages/styles livekit-client: specifier: 'catalog:' - version: 2.15.8(@types/dom-mediacapture-record@1.0.22) + version: 2.15.14(@types/dom-mediacapture-record@1.0.22) react: specifier: ^18.2.0 version: 18.3.1 @@ -160,8 +163,8 @@ importers: specifier: ^6.0.12 version: 6.1.0 livekit-client: - specifier: 'catalog:' - version: 2.15.8(@types/dom-mediacapture-record@1.0.22) + specifier: ^2.15.14 + version: 2.15.14(@types/dom-mediacapture-record@1.0.22) livekit-server-sdk: specifier: ^2.13.0 version: 2.13.3 @@ -282,7 +285,7 @@ importers: version: 6.1.0 livekit-client: specifier: 'catalog:' - version: 2.15.8(@types/dom-mediacapture-record@1.0.22) + version: 2.15.14(@types/dom-mediacapture-record@1.0.22) livekit-server-sdk: specifier: ^2.13.0 version: 2.13.3 @@ -379,10 +382,10 @@ importers: version: link:../../packages/styles '@livekit/track-processors': specifier: ^0.3.2 - version: 0.3.2(livekit-client@2.15.8(@types/dom-mediacapture-record@1.0.22)) + version: 0.3.2(livekit-client@2.15.14(@types/dom-mediacapture-record@1.0.22)) livekit-client: specifier: 'catalog:' - version: 2.15.8(@types/dom-mediacapture-record@1.0.22) + version: 2.15.14(@types/dom-mediacapture-record@1.0.22) livekit-server-sdk: specifier: ^2.6.1 version: 2.6.1 @@ -422,7 +425,7 @@ importers: version: 1.6.13 livekit-client: specifier: 'catalog:' - version: 2.15.8(@types/dom-mediacapture-record@1.0.22) + version: 2.15.14(@types/dom-mediacapture-record@1.0.22) loglevel: specifier: 1.9.1 version: 1.9.1 @@ -471,7 +474,7 @@ importers: version: link:../core '@livekit/krisp-noise-filter': specifier: ^0.2.12 || ^0.3.0 - version: 0.2.12(livekit-client@2.15.8(@types/dom-mediacapture-record@1.0.22)) + version: 0.2.12(livekit-client@2.15.14(@types/dom-mediacapture-record@1.0.22)) clsx: specifier: 2.1.1 version: 2.1.1 @@ -483,7 +486,7 @@ importers: version: 6.1.0 livekit-client: specifier: 'catalog:' - version: 2.15.8(@types/dom-mediacapture-record@1.0.22) + version: 2.15.14(@types/dom-mediacapture-record@1.0.22) tslib: specifier: ^2.6.2 version: 2.8.1 @@ -2552,6 +2555,9 @@ packages: '@livekit/protocol@1.42.0': resolution: {integrity: sha512-42sYSCay2PZrn5yHHt+O3RQpTElcTrA7bqg7iYbflUApeerA5tUCJDr8Z4abHsYHVKjqVUbkBq/TPmT3X6aYOQ==} + '@livekit/protocol@1.42.2': + resolution: {integrity: sha512-0jeCwoMJKcwsZICg5S6RZM4xhJoF78qMvQELjACJQn6/VB+jmiySQKOSELTXvPBVafHfEbMlqxUw2UR1jTXs2g==} + '@livekit/track-processors@0.3.2': resolution: {integrity: sha512-4JUCzb7yIKoVsTo8J6FTzLZJHcI6DihfX/pGRDg0SOGaxprcDPrt8jaDBBTsnGBSXHeMxl2ugN+xQjdCWzLKEA==} peerDependencies: @@ -7204,8 +7210,8 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - livekit-client@2.15.8: - resolution: {integrity: sha512-M+GnlmoY+JOfGGhDov5f4V273YZ9DuWFBaPwz42fliC3TsFTzEcJoRqqE7uLtEGAnloqbLPk+sIvW/XSU4Z4/Q==} + livekit-client@2.15.14: + resolution: {integrity: sha512-q3QY1Md6+2l4LpV7OPSrKYbuMfMoEbcu+UaJL2e8Btrkh7R2wGJzWh8A852Stx4It1508IP9PK4q7U6trDzvYA==} peerDependencies: '@types/dom-mediacapture-record': ^1 @@ -9691,7 +9697,7 @@ snapshots: '@babel/types': 7.26.8 '@types/gensync': 1.0.4 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -10436,7 +10442,7 @@ snapshots: '@babel/parser': 7.26.8 '@babel/template': 7.26.8 '@babel/types': 7.26.8 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -11465,9 +11471,9 @@ snapshots: transitivePeerDependencies: - encoding - '@livekit/krisp-noise-filter@0.2.12(livekit-client@2.15.8(@types/dom-mediacapture-record@1.0.22))': + '@livekit/krisp-noise-filter@0.2.12(livekit-client@2.15.14(@types/dom-mediacapture-record@1.0.22))': dependencies: - livekit-client: 2.15.8(@types/dom-mediacapture-record@1.0.22) + livekit-client: 2.15.14(@types/dom-mediacapture-record@1.0.22) '@livekit/mutex@1.1.1': {} @@ -11479,11 +11485,15 @@ snapshots: dependencies: '@bufbuild/protobuf': 1.10.1 - '@livekit/track-processors@0.3.2(livekit-client@2.15.8(@types/dom-mediacapture-record@1.0.22))': + '@livekit/protocol@1.42.2': + dependencies: + '@bufbuild/protobuf': 1.10.1 + + '@livekit/track-processors@0.3.2(livekit-client@2.15.14(@types/dom-mediacapture-record@1.0.22))': dependencies: '@mediapipe/holistic': 0.5.1675471629 '@mediapipe/tasks-vision': 0.10.9 - livekit-client: 2.15.8(@types/dom-mediacapture-record@1.0.22) + livekit-client: 2.15.14(@types/dom-mediacapture-record@1.0.22) '@manypkg/find-root@1.1.0': dependencies: @@ -15415,10 +15425,6 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.0: - dependencies: - ms: 2.1.3 - debug@4.4.0(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -17070,7 +17076,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -17084,7 +17090,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -17961,10 +17967,10 @@ snapshots: lines-and-columns@1.2.4: {} - livekit-client@2.15.8(@types/dom-mediacapture-record@1.0.22): + livekit-client@2.15.14(@types/dom-mediacapture-record@1.0.22): dependencies: '@livekit/mutex': 1.1.1 - '@livekit/protocol': 1.42.0 + '@livekit/protocol': 1.42.2 '@types/dom-mediacapture-record': 1.0.22 events: 3.3.0 jose: 6.1.0 @@ -20191,7 +20197,7 @@ snapshots: vite-node@3.0.5(@types/node@24.7.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.84.0)(terser@5.38.1)(tsx@4.19.2)(yaml@2.4.5): dependencies: cac: 6.7.14 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) es-module-lexer: 1.6.0 pathe: 2.0.2 vite: 6.3.5(@types/node@24.7.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.84.0)(terser@5.38.1)(tsx@4.19.2)(yaml@2.4.5) @@ -20216,7 +20222,7 @@ snapshots: '@volar/typescript': 2.4.11 '@vue/language-core': 2.2.0(typescript@5.8.2) compare-versions: 6.1.1 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) kolorist: 1.8.0 local-pkg: 0.5.1 magic-string: 0.30.17 @@ -20256,7 +20262,7 @@ snapshots: '@vitest/spy': 3.0.5 '@vitest/utils': 3.0.5 chai: 5.1.2 - debug: 4.4.0 + debug: 4.4.0(supports-color@5.5.0) expect-type: 1.1.0 magic-string: 0.30.17 pathe: 2.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c1fae8909..b188abd32 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,5 +5,5 @@ packages: - 'tooling/*' catalog: - livekit-client: ^2.15.8 + livekit-client: ^2.15.14 "@livekit/protocol": ^1.42.0