@lofcz/orb-ui 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +330 -0
  3. package/dist/adapters/audio-level.d.ts +28 -0
  4. package/dist/adapters/elevenlabs/index.d.ts +85 -0
  5. package/dist/adapters/gemini-live/index.d.ts +70 -0
  6. package/dist/adapters/index.d.ts +8 -0
  7. package/dist/adapters/livekit/browser.d.ts +49 -0
  8. package/dist/adapters/livekit/index.d.ts +136 -0
  9. package/dist/adapters/openai-realtime/index.d.ts +37 -0
  10. package/dist/adapters/pipecat/index.d.ts +62 -0
  11. package/dist/adapters/types.d.ts +7 -0
  12. package/dist/adapters/vapi/index.d.ts +42 -0
  13. package/dist/adapters.cjs +1 -0
  14. package/dist/adapters.js +829 -0
  15. package/dist/components/Orb/Orb.d.ts +2 -0
  16. package/dist/components/Orb/Orb.types.d.ts +87 -0
  17. package/dist/components/Orb/index.d.ts +2 -0
  18. package/dist/components/Orb/signals.d.ts +3 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/livekit-D-s4sYba.cjs +1 -0
  21. package/dist/livekit-Z3IMmWaG.js +316 -0
  22. package/dist/livekit-adapter.cjs +1 -0
  23. package/dist/livekit-adapter.js +30 -0
  24. package/dist/orb-ui.cjs +203 -0
  25. package/dist/orb-ui.js +984 -0
  26. package/dist/themes/bars/BarsTheme.d.ts +14 -0
  27. package/dist/themes/bars/index.d.ts +1 -0
  28. package/dist/themes/circle/CircleTheme.d.ts +14 -0
  29. package/dist/themes/circle/index.d.ts +1 -0
  30. package/dist/themes/cloud/CloudTheme.d.ts +14 -0
  31. package/dist/themes/cloud/index.d.ts +1 -0
  32. package/dist/themes/debug/DebugTheme.d.ts +15 -0
  33. package/dist/themes/debug/index.d.ts +1 -0
  34. package/dist/themes/index.d.ts +5 -0
  35. package/dist/themes/radial/RadialTheme.d.ts +13 -0
  36. package/dist/themes/radial/index.d.ts +1 -0
  37. package/package.json +122 -0
@@ -0,0 +1,136 @@
1
+ import { OrbAdapter } from '../types.js';
2
+ import { OutputVolumeCalibrationSource, OutputVolumeSample } from '../audio-level.js';
3
+ interface LKAnalyserResult {
4
+ calculateVolume: () => number;
5
+ cleanup: () => Promise<void>;
6
+ }
7
+ interface LKAudioTrack {
8
+ mediaStreamTrack: MediaStreamTrack;
9
+ }
10
+ interface LKRemoteAudioTrack extends LKAudioTrack {
11
+ attach(): HTMLMediaElement;
12
+ detach(): HTMLMediaElement[];
13
+ }
14
+ interface LKTrackPublication<TTrack extends LKAudioTrack = LKRemoteAudioTrack> {
15
+ track?: TTrack;
16
+ source: string;
17
+ }
18
+ interface LKParticipant {
19
+ isLocal: boolean;
20
+ attributes?: Record<string, string>;
21
+ getTrackPublication(source: string): LKTrackPublication | undefined;
22
+ kind?: number;
23
+ }
24
+ interface LKLocalParticipant {
25
+ setMicrophoneEnabled(enabled: boolean): Promise<LKTrackPublication<LKAudioTrack> | undefined>;
26
+ getTrackPublication?(source: string): LKTrackPublication<LKAudioTrack> | undefined;
27
+ }
28
+ interface LKRoom {
29
+ connect(serverUrl: string, token: string, options?: Record<string, unknown>): Promise<void>;
30
+ disconnect(): void;
31
+ on: unknown;
32
+ off?: unknown;
33
+ removeListener?: unknown;
34
+ remoteParticipants: Map<string, LKParticipant>;
35
+ localParticipant: LKLocalParticipant;
36
+ state: string;
37
+ }
38
+ interface LKCreateAudioAnalyser<TTrack = unknown> {
39
+ (track: TTrack, options?: Record<string, unknown>): LKAnalyserResult;
40
+ }
41
+ export type LiveKitTokenOptionValue = string | (() => string);
42
+ export interface LiveKitConnectionDetails {
43
+ /** LiveKit server URL, e.g. "wss://your-project.livekit.cloud". */
44
+ serverUrl: string;
45
+ /** Short-lived participant token generated by LiveKit or your token endpoint. */
46
+ participantToken: string;
47
+ }
48
+ export interface LiveKitResolvedTokenOptions {
49
+ roomName?: string;
50
+ participantName?: string;
51
+ participantIdentity?: string;
52
+ participantMetadata?: string;
53
+ participantAttributes?: Record<string, string>;
54
+ agentName?: string;
55
+ agentMetadata?: string;
56
+ deployment?: string;
57
+ }
58
+ export interface LiveKitTokenOptions {
59
+ roomName?: LiveKitTokenOptionValue;
60
+ participantName?: LiveKitTokenOptionValue;
61
+ participantIdentity?: LiveKitTokenOptionValue;
62
+ participantMetadata?: LiveKitTokenOptionValue;
63
+ participantAttributes?: Record<string, string> | (() => Record<string, string>);
64
+ agentName?: LiveKitTokenOptionValue;
65
+ agentMetadata?: LiveKitTokenOptionValue;
66
+ deployment?: LiveKitTokenOptionValue;
67
+ }
68
+ export interface LiveKitTokenSource {
69
+ fetch(options?: LiveKitResolvedTokenOptions, force?: boolean): Promise<LiveKitConnectionDetails>;
70
+ }
71
+ interface LiveKitVolumeConfig {
72
+ /** Optional live-tunable output shaping. A getter is read for every meter sample. */
73
+ outputVolumeCalibration?: OutputVolumeCalibrationSource;
74
+ /** Receives raw, shaped, and smoothed output levels for diagnostics. */
75
+ onOutputVolumeSample?: (sample: OutputVolumeSample) => void;
76
+ }
77
+ interface LiveKitManagedBaseConfig<TTrack = unknown> extends LiveKitVolumeConfig {
78
+ /** The createAudioAnalyser function from livekit-client. */
79
+ createAudioAnalyser: LKCreateAudioAnalyser<TTrack>;
80
+ /** The Room constructor from livekit-client. */
81
+ RoomClass: new (...args: never[]) => LKRoom;
82
+ /** LiveKit room.connect options passed through unchanged. */
83
+ connectOptions?: Record<string, unknown>;
84
+ /** Enable the local microphone after connecting. Defaults to true. */
85
+ enableMicrophone?: boolean;
86
+ /** Options passed to token sources or getConnectionDetails. Values may be resolved per start. */
87
+ tokenOptions?: LiveKitTokenOptions;
88
+ }
89
+ /** App-managed mode: subscribe to a pre-existing Room. */
90
+ interface LiveKitRoomConfig<TTrack = unknown> extends LiveKitVolumeConfig {
91
+ /** A connected Room instance from livekit-client. */
92
+ room: LKRoom;
93
+ /** The createAudioAnalyser function from livekit-client. */
94
+ createAudioAnalyser: LKCreateAudioAnalyser<TTrack>;
95
+ }
96
+ /** Recommended mode: fetch connection details when the orb starts. */
97
+ interface LiveKitConnectionDetailsConfig<TTrack = unknown> extends LiveKitManagedBaseConfig<TTrack> {
98
+ getConnectionDetails(options: LiveKitResolvedTokenOptions): Promise<LiveKitConnectionDetails>;
99
+ tokenSource?: never;
100
+ serverUrl?: never;
101
+ participantToken?: never;
102
+ }
103
+ /** Convenience mode: use a LiveKit TokenSource such as TokenSource.endpoint or sandboxTokenServer. */
104
+ interface LiveKitTokenSourceConfig<TTrack = unknown> extends LiveKitManagedBaseConfig<TTrack> {
105
+ tokenSource: LiveKitTokenSource;
106
+ getConnectionDetails?: never;
107
+ serverUrl?: never;
108
+ participantToken?: never;
109
+ }
110
+ /** Advanced mode: pass already-generated static credentials. */
111
+ interface LiveKitStaticConnectionConfig<TTrack = unknown> extends LiveKitManagedBaseConfig<TTrack> {
112
+ /** LiveKit server URL, e.g. "wss://your-project.livekit.cloud". */
113
+ serverUrl: string;
114
+ /** Short-lived participant token. Generate this on your server. */
115
+ participantToken: string;
116
+ getConnectionDetails?: never;
117
+ tokenSource?: never;
118
+ }
119
+ type LiveKitManagedConfig<TTrack = unknown> = LiveKitConnectionDetailsConfig<TTrack> | LiveKitTokenSourceConfig<TTrack> | LiveKitStaticConnectionConfig<TTrack>;
120
+ export type LiveKitAdapterConfig<TTrack = unknown> = LiveKitRoomConfig<TTrack> | LiveKitManagedConfig<TTrack>;
121
+ export interface LiveKitOrbAdapter extends OrbAdapter {
122
+ /** Connect to the LiveKit room and start tracking agent state. */
123
+ start(): Promise<void>;
124
+ /** Disconnect from the LiveKit room. */
125
+ stop(): Promise<void>;
126
+ }
127
+ /**
128
+ * Creates an OrbAdapter for LiveKit Agents.
129
+ *
130
+ * Managed modes create and own a Room. App-managed mode listens to a Room that
131
+ * your app already connected. The adapter emits signal-native orb-ui state,
132
+ * including LiveKit `thinking` as `OrbState` `"thinking"`, local microphone
133
+ * audio as `inputVolume`, and agent audio as `outputVolume`.
134
+ */
135
+ export declare function createLiveKitAdapter<TTrack = unknown>(config: LiveKitAdapterConfig<TTrack>): LiveKitOrbAdapter | OrbAdapter;
136
+ export {};
@@ -0,0 +1,37 @@
1
+ import { OrbAdapter } from '../types.js';
2
+ import { OutputVolumeCalibrationSource, OutputVolumeSample } from '../audio-level.js';
3
+ export interface OpenAIRealtimeClientSecret {
4
+ value: string;
5
+ }
6
+ export interface OpenAIRealtimeAdapterConfig {
7
+ /** Fetch a newly minted Realtime client secret for every session start. */
8
+ getClientSecret(): Promise<string | OpenAIRealtimeClientSecret>;
9
+ /** Defaults to OpenAI's GA WebRTC calls endpoint. */
10
+ callsUrl?: string;
11
+ /** Defaults to `{ audio: true }`. */
12
+ mediaStreamConstraints?: MediaStreamConstraints;
13
+ /** Runtime override for tests, React Native shims, or custom browser wrappers. */
14
+ getUserMedia?: (constraints: MediaStreamConstraints) => Promise<MediaStream>;
15
+ /** Runtime override for custom WebRTC implementations. */
16
+ createPeerConnection?: () => RTCPeerConnection;
17
+ /** Runtime override for custom fetch implementations. */
18
+ fetch?: typeof fetch;
19
+ /** Runtime override for custom audio element ownership. */
20
+ createAudioElement?: () => HTMLAudioElement;
21
+ /** Return undefined to disable browser-side volume metering. */
22
+ createAudioContext?: () => AudioContext | undefined;
23
+ /** Optional live-tunable output shaping. A getter is read for every meter sample. */
24
+ outputVolumeCalibration?: OutputVolumeCalibrationSource;
25
+ /** Receives raw, shaped, and smoothed output levels for diagnostics. */
26
+ onOutputVolumeSample?: (sample: OutputVolumeSample) => void;
27
+ }
28
+ export interface OpenAIRealtimeOrbAdapter extends OrbAdapter {
29
+ start(): Promise<void>;
30
+ stop(): Promise<void>;
31
+ }
32
+ /**
33
+ * Creates a managed browser WebRTC adapter for the OpenAI Realtime API.
34
+ * Standard API keys stay on the app's server; the adapter only receives a
35
+ * short-lived client secret.
36
+ */
37
+ export declare function createOpenAIRealtimeAdapter(config: OpenAIRealtimeAdapterConfig): OpenAIRealtimeOrbAdapter;
@@ -0,0 +1,62 @@
1
+ import { OrbAdapter } from '../types.js';
2
+ import { AudioContextSource, OutputVolumeCalibrationSource, OutputVolumeSample } from '../audio-level.js';
3
+ type PipecatListener = (...args: unknown[]) => void;
4
+ /** Minimal PipecatClient surface used by orb-ui. */
5
+ export interface PipecatClientLike {
6
+ on(event: string, listener: PipecatListener): unknown;
7
+ off?(event: string, listener: PipecatListener): unknown;
8
+ removeListener?(event: string, listener: PipecatListener): unknown;
9
+ connect(...args: unknown[]): Promise<unknown>;
10
+ disconnect(): void | Promise<void>;
11
+ state?: string;
12
+ tracks?(): PipecatTracksLike;
13
+ }
14
+ export interface PipecatParticipantLike {
15
+ id?: string;
16
+ name?: string;
17
+ local?: boolean;
18
+ }
19
+ export interface PipecatTracksLike {
20
+ local?: {
21
+ audio?: MediaStreamTrack;
22
+ };
23
+ bot?: {
24
+ audio?: MediaStreamTrack;
25
+ };
26
+ }
27
+ export interface PipecatAdapterOptions {
28
+ /**
29
+ * Starts and connects the Pipecat client. Override this for transport-specific
30
+ * connection params or `startBotAndConnect()` endpoints.
31
+ */
32
+ connect?: () => void | Promise<unknown>;
33
+ /** Disconnects the Pipecat client. Defaults to `client.disconnect()`. */
34
+ disconnect?: () => void | Promise<void>;
35
+ /**
36
+ * Optional filter for multi-participant transports. Remote audio from local
37
+ * participants is ignored automatically.
38
+ */
39
+ isBotParticipant?: (participant: PipecatParticipantLike) => boolean;
40
+ /** Play remote bot audio tracks automatically. Defaults to true. */
41
+ playRemoteAudio?: boolean;
42
+ /** Runtime override for custom audio element ownership. */
43
+ createAudioElement?: () => HTMLAudioElement;
44
+ /** Return undefined to disable browser-track volume metering. */
45
+ createAudioContext?: AudioContextSource;
46
+ /** Optional live-tunable output shaping. A getter is read for every meter sample. */
47
+ outputVolumeCalibration?: OutputVolumeCalibrationSource;
48
+ /** Receives raw, shaped, and smoothed output levels for diagnostics. */
49
+ onOutputVolumeSample?: (sample: OutputVolumeSample) => void;
50
+ }
51
+ export interface PipecatOrbAdapter extends OrbAdapter {
52
+ start(): Promise<void>;
53
+ stop(): Promise<void>;
54
+ }
55
+ /**
56
+ * Creates a signal-native adapter for Pipecat's JavaScript client.
57
+ *
58
+ * The adapter is transport-agnostic: Daily, SmallWebRTC, Pipecat Cloud, and
59
+ * custom transports all expose the same RTVI events through `PipecatClient`.
60
+ */
61
+ export declare function createPipecatAdapter(client: PipecatClientLike, options?: PipecatAdapterOptions): PipecatOrbAdapter;
62
+ export {};
@@ -0,0 +1,7 @@
1
+ import { OrbAdapter, OrbSignal, OrbSignalListener, OrbState } from '../components/Orb/Orb.types.js';
2
+ export type { OrbAdapter, OrbSignal, OrbSignalListener, OrbState };
3
+ /**
4
+ * Shared mic volume curve — rescale real-world mic range to 0–1 with pow shaping
5
+ * for custom provider adapters that need browser-side input metering.
6
+ */
7
+ export declare function normalizeMicVolume(v: number): number;
@@ -0,0 +1,42 @@
1
+ import { OrbAdapter } from '../types.js';
2
+ interface VapiClient {
3
+ on(event: 'call-start', listener: () => void): void;
4
+ on(event: 'call-end', listener: () => void): void;
5
+ on(event: 'speech-start', listener: () => void): void;
6
+ on(event: 'speech-end', listener: () => void): void;
7
+ on(event: 'volume-level', listener: (volume: number) => void): void;
8
+ on(event: 'message', listener: (message: VapiMessage) => void): void;
9
+ on(event: 'error', listener: (error: unknown) => void): void;
10
+ removeListener(event: string, listener: (...args: unknown[]) => void): void;
11
+ start(...args: any[]): Promise<unknown>;
12
+ stop(): void;
13
+ }
14
+ interface VapiMessage {
15
+ type: string;
16
+ role?: string;
17
+ transcriptType?: 'partial' | 'final';
18
+ transcript?: string;
19
+ }
20
+ /**
21
+ * Creates an OrbAdapter for Vapi voice agents.
22
+ *
23
+ * State mapping:
24
+ * vapi.start() called (intercepted) → 'connecting'
25
+ * call-start → 'listening'
26
+ * speech-start → 'speaking'
27
+ * speech-end → 'listening' (debounced 350 ms)
28
+ * call-end → 'idle'
29
+ * error → 'error'
30
+ *
31
+ * Volume: raw Vapi values are normalized (noise gate + EMA) before being emitted
32
+ * as outputVolume while the assistant is speaking.
33
+ *
34
+ * @param client - A Vapi instance from @vapi-ai/web
35
+ * @param options - Optional config (e.g. assistantId to pass to vapi.start())
36
+ */
37
+ interface VapiAdapterOptions {
38
+ /** Assistant ID passed to vapi.start() when the orb is clicked. */
39
+ assistantId?: string;
40
+ }
41
+ export declare function createVapiAdapter(client: VapiClient, options?: VapiAdapterOptions): OrbAdapter;
42
+ export {};
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./livekit-D-s4sYba.cjs");var t=.12;function n(e){let t=`idle`,n=null;function r(r){if(n&&=(clearTimeout(n),null),t===`speaking`&&r===`listening`){n=setTimeout(()=>{t=r,e(r),n=null},350);return}t=r,e(r)}function i(){n&&=(clearTimeout(n),null)}return{emitState:r,clearTimer:i}}function r(e,r){let i=new Set,a=null;function o(){a||(a=e.start.bind(e),e.start=async(...e)=>(i.forEach(e=>e()),a(...e)))}function s(){i.size>0||!a||(e.start=a,a=null)}return{async start(){await e.start(r?.assistantId)},stop(){e.stop()},subscribe(r){let a={state:`idle`,volume:0,outputVolume:0},c=0;function l(e){let n=e<t?0:(e-t)/.88,r=n>c?.8:.5;return c+=(n-c)*r,c}function u(e){a=e,r(e)}function d(e){u({...a,...e,state:e.state??a.state})}let{emitState:f,clearTimer:p}=n(e=>d({state:e})),m=()=>f(`connecting`),h=`idle`,g=!1,_=()=>{g=!0,h=`listening`,f(`listening`),d({volume:0,outputVolume:0})},v=()=>{g=!1,h=`idle`,E(),f(`idle`),d({volume:0,outputVolume:0}),c=0},y=()=>{g&&(h=`speaking`,f(`speaking`),d({volume:0,outputVolume:0}),T())},b=()=>{g&&(E(),h=`listening`,d({volume:0,outputVolume:0}),c=0,f(`listening`))},x=0,S=0,C=0,w=()=>{h===`speaking`&&(S+=(x-S)*.1,d({volume:S,outputVolume:S})),C=requestAnimationFrame(w)},T=()=>{C||=requestAnimationFrame(w)},E=()=>{C&&=(cancelAnimationFrame(C),0),S=0,x=0},D=e=>{if(h===`speaking`){let t=l(e);x=t/(t+.3)}},O=()=>{},k=e=>{console.error(`[orb-ui/vapi] Error:`,e),g=!1,h=`error`,p(),E(),d({state:`error`,volume:0,outputVolume:0,error:e}),c=0};return e.on(`call-start`,_),e.on(`call-end`,v),e.on(`speech-start`,y),e.on(`speech-end`,b),e.on(`volume-level`,D),e.on(`message`,O),e.on(`error`,k),i.add(m),o(),()=>{p(),E(),c=0,e.removeListener(`call-start`,_),e.removeListener(`call-end`,v),e.removeListener(`speech-start`,y),e.removeListener(`speech-end`,b),e.removeListener(`volume-level`,D),e.removeListener(`message`,O),e.removeListener(`error`,k),i.delete(m),s()}}}}function i(e,t){let n=null,r=0,i=null,a=null,o={state:`idle`,volume:0,inputVolume:0,outputVolume:0},s=`idle`,c=new Set;function l(e){o=e,c.forEach(t=>t(e))}function u(e){o={...o,...e,state:e.state??o.state},l(o)}function d(e){s=e,u({state:e})}function f(){a||=setInterval(()=>{if(n){if(s===`listening`){let e=Math.min(n.getInputVolume()*2,1);u({volume:e,inputVolume:e})}else if(s===`speaking`){let e=Math.min(n.getOutputVolume()*2,1);u({volume:e,outputVolume:e})}}},33)}function p(){a&&=(clearInterval(a),null)}function m(){p(),u({volume:0,inputVolume:0,outputVolume:0})}function h(){s=`idle`,u({state:`idle`,volume:0,inputVolume:0,outputVolume:0})}function g(e){return e===r}function _(e){return{onStatusChange:({status:t})=>{g(e)&&t===`connecting`&&d(`connecting`)},onConnect:()=>{g(e)&&(d(`listening`),f())},onModeChange:({mode:t})=>{g(e)&&(t===`speaking`?(d(`speaking`),f()):(d(`listening`),f()))},onDisconnect:()=>{g(e)&&(p(),n=null,h())},onError:t=>{g(e)&&(console.error(`[orb-ui/elevenlabs] Error:`,t),p(),s=`error`,u({state:`error`,volume:0,inputVolume:0,outputVolume:0,error:t}),n=null)}}}return{subscribe(e){return c.add(e),e(o),n&&(s===`listening`||s===`speaking`)&&f(),()=>{c.delete(e),c.size===0&&m()}},async start(){if(n||i)return i??void 0;r+=1;let a=r;return i=(async()=>{try{let r=await e.startSession({...t,..._(a)});if(!g(a)){await r.endSession().catch(e=>{console.error(`[orb-ui/elevenlabs] stale session cleanup failed:`,e)});return}n=r}catch(e){if(!g(a))return;console.error(`[orb-ui/elevenlabs] startSession failed:`,e),d(`error`),u({volume:0,inputVolume:0,outputVolume:0,error:e})}finally{i=null}})(),i},async stop(){let e=n;if(!e){i&&(r+=1,p(),h(),await i);return}let t=r;p();try{await e.endSession()}catch(e){console.error(`[orb-ui/elevenlabs] endSession failed:`,e)}n===e&&g(t)&&(n=null,h())}}}var a={connected:`connected`,disconnected:`disconnected`,transportStateChanged:`transportStateChanged`,botReady:`botReady`,botDisconnected:`botDisconnected`,error:`error`,messageError:`messageError`,deviceError:`deviceError`,localAudioLevel:`localAudioLevel`,remoteAudioLevel:`remoteAudioLevel`,botStartedSpeaking:`botStartedSpeaking`,botStoppedSpeaking:`botStoppedSpeaking`,userStartedSpeaking:`userStartedSpeaking`,userStoppedSpeaking:`userStoppedSpeaking`,botLlmStarted:`botLlmStarted`,botTtsStarted:`botTtsStarted`},o=.002,s=4,c=.8,l=.6,u=.2,d={noiseFloor:.002,gain:5.1,exponent:.8,attack:.5,release:.22};function f(){if(typeof window>`u`)return;let e=window.AudioContext;return e?new e:void 0}function p(e){switch(e){case`initializing`:case`initialized`:case`authenticating`:case`authenticated`:case`connecting`:case`connected`:return`connecting`;case`ready`:return`listening`;case`disconnected`:case`disconnecting`:return`idle`;case`error`:return`error`;default:return}}function m(e){if(typeof e!=`number`||!Number.isFinite(e)||e<=o)return 0;let t=Math.min(1,Math.max(0,e-o));return Math.min(1,t*s)**+c}function h(e,t){let n=m(e),r=n>t?l:u;return t+(n-t)*r}function g(e){return typeof e==`object`&&!!e&&`kind`in e&&typeof e.kind==`string`}function _(t,n={}){let r=new Set,i=new Map,o={state:p(t.state??``)??`idle`,volume:0,inputVolume:0,outputVolume:0},s=!1,c=0,l=0,u=null,m=null,_,v;function y(){let e=typeof n.outputVolumeCalibration==`function`?n.outputVolumeCalibration():n.outputVolumeCalibration;return{...d,...e}}function b(e){o=e,r.forEach(t=>t(e))}function x(e,t){c=0,l=0,b({state:e,volume:0,inputVolume:0,outputVolume:0,...t===void 0?{}:{error:t}})}function S(e){if(o.state!==`listening`)return;c=h(e,c);let t=c;b({...o,volume:t,inputVolume:t,outputVolume:0})}function C(t,r){if(o.state!==`speaking`)return;if(r&&typeof r==`object`){let e=r;if(e.local||n.isBotParticipant&&!n.isBotParticipant(e))return}let i=e.r(typeof t==`number`?t:0,l,y);l=i.normalized,n.onOutputVolumeSample?.(i);let a=i.normalized;b({...o,volume:a,inputVolume:0,outputVolume:a})}function w(){u=null,_?.stop(),_=void 0}function T(){m=null,v?.stop(),v=void 0}function E(){w(),T()}function D(t){u===t&&_||(w(),u=t,_=e.i(t,n.createAudioContext??f,S),_||(u=null))}function O(t){m===t&&v||(T(),m=t,v=e.i(t,n.createAudioContext??f,C),v||(m=null))}function k(){let e=t.tracks?.();e?.local?.audio&&D(e.local.audio),e?.bot?.audio&&O(e.bot.audio)}function A(e){if(!e||typeof e!=`object`)return!0;let t=e;return t.local?!1:n.isBotParticipant?.(t)??!0}function j(e,t){if(n.playRemoteAudio===!1||!g(e)||e.kind!==`audio`||!A(t))return;let r=n.createAudioElement?.()??new Audio;r.autoplay=!0,r.srcObject=new MediaStream([e]),i.set(e,r),r.play().catch(()=>void 0)}function M(e){if(!g(e))return;let t=i.get(e);t&&(t.pause(),t.srcObject=null,t.remove(),i.delete(e))}function N(e,n){if(!g(e)||e.kind!==`audio`)return;let r=t.tracks?.().local?.audio;n?.local===!0||e===r?D(e):A(n)&&O(e),j(e,n)}function P(e){e===u&&w(),e===m&&T(),M(e)}function F(){i.forEach(e=>{e.pause(),e.srcObject=null,e.remove()}),i.clear()}function I(){E(),x(`idle`)}function L(){x(`listening`),k()}let R=[[a.connected,()=>x(`connecting`)],[a.botReady,L],[a.disconnected,I],[a.botDisconnected,I],[a.transportStateChanged,e=>{if(typeof e!=`string`)return;let t=p(e);t&&x(t)}],[a.userStartedSpeaking,()=>x(`listening`)],[a.userStoppedSpeaking,()=>{o.state===`listening`&&x(`thinking`)}],[a.botLlmStarted,()=>x(`thinking`)],[a.botTtsStarted,()=>x(`thinking`)],[a.botStartedSpeaking,()=>x(`speaking`)],[a.botStoppedSpeaking,()=>x(`listening`)],[a.localAudioLevel,e=>!_&&S(e)],[a.remoteAudioLevel,(e,t)=>!v&&C(e,t)],[`trackStarted`,N],[`trackStopped`,P],[a.error,e=>x(`error`,e)],[a.messageError,e=>x(`error`,e)],[a.deviceError,e=>x(`error`,e)]];function z(){s||(s=!0,R.forEach(([e,n])=>t.on(e,n)))}function B(){s&&(s=!1,R.forEach(([e,n])=>{t.off?t.off(e,n):t.removeListener?.(e,n)}),E(),F())}return{subscribe(e){return r.add(e),z(),e(o),()=>{r.delete(e),r.size===0&&B()}},async start(){x(`connecting`);try{await(n.connect?n.connect():t.connect())}catch(e){throw x(`error`,e),e}},async stop(){try{await(n.disconnect?n.disconnect():t.disconnect())}finally{E(),F(),x(`idle`)}}}}var v=`https://api.openai.com/v1/realtime/calls`,y=.015,b=8,x={noiseFloor:.003,gain:4,exponent:.8,attack:.55,release:.1};function S(){let e=window.AudioContext;return e?new e:void 0}function C(e){return Math.min(1,Math.max(0,(e*4)**.8))}function w(e,t,n){try{let r=t();if(!r)return;let i=r.createMediaStreamSource(e),a=r.createAnalyser();a.fftSize=512,a.smoothingTimeConstant=.25,i.connect(a);let o=new Float32Array(a.fftSize),s=setInterval(()=>{a.getFloatTimeDomainData(o);let e=0;for(let t of o)e+=t*t;n(Math.sqrt(e/o.length))},33);return{async stop(){clearInterval(s),i.disconnect(),a.disconnect(),r.state!==`closed`&&await r.close()}}}catch{return}}function T(e){let t=typeof e==`string`?e:e.value;if(!t)throw Error(`[orb-ui/openai-realtime] getClientSecret returned an empty value.`);return t}function E(t){let n=new Set,r={state:`idle`,volume:0,inputVolume:0,outputVolume:0},i=null,a=null,o=null,s=null,c,l,u=0,d=0,f=!1;function p(){let e=typeof t.outputVolumeCalibration==`function`?t.outputVolumeCalibration():t.outputVolumeCalibration;return{...x,...e}}function m(e){r=e,n.forEach(t=>t(e))}function h(e,t){(r.state!==e||t!==void 0)&&(e!==`speaking`&&(d=0),m({state:e,volume:0,inputVolume:0,outputVolume:0,...t===void 0?{}:{error:t}}))}function g(e){if(r.state!==`listening`)return;let t=C(e);m({...r,volume:t,inputVolume:t,outputVolume:0})}function _(n){let i=e.r(n,d,p);d=i.normalized,t.onOutputVolumeSample?.(i);let a=i.normalized;if(a>y)u=0,r.state!==`speaking`&&h(`speaking`);else if(r.state===`speaking`&&(u+=1,u>=b)){u=0,h(`listening`);return}r.state===`speaking`&&m({...r,volume:a,inputVolume:0,outputVolume:a})}function E(e){switch(e.type){case`session.created`:case`session.updated`:h(`listening`);break;case`input_audio_buffer.speech_started`:h(`listening`);break;case`input_audio_buffer.speech_stopped`:case`response.created`:h(`thinking`);break;case`response.output_audio.delta`:case`output_audio_buffer.started`:h(`speaking`);break;case`output_audio_buffer.stopped`:h(`listening`);break;case`response.done`:case`response.cancelled`:r.state!==`speaking`&&h(`listening`);break;case`error`:h(`error`,e.error??e)}}async function D(e){f=!0,a?.close(),a=null,i?.close(),i=null,o?.getTracks().forEach(e=>e.stop()),o=null,s?.pause(),s&&(s.srcObject=null),s?.remove(),s=null,await Promise.all([c?.stop(),l?.stop()]),c=void 0,l=void 0,u=0,d=0,e&&h(`idle`),f=!1}return{subscribe(e){return n.add(e),e(r),()=>n.delete(e)},async start(){if(!i){h(`connecting`),f=!1;try{let e=t.getUserMedia??(e=>navigator.mediaDevices.getUserMedia(e)),n=t.createPeerConnection??(()=>new RTCPeerConnection),r=t.fetch??fetch,u=t.createAudioElement??(()=>new Audio),d=t.createAudioContext??S,p=T(await t.getClientSecret());o=await e(t.mediaStreamConstraints??{audio:!0}),c=w(o,d,g);let m=n();i=m,o.getAudioTracks().forEach(e=>m.addTrack(e,o)),s=u(),s.autoplay=!0,m.ontrack=e=>{let t=e.streams[0]??new MediaStream([e.track]);s&&(s.srcObject=t),s?.play().catch(()=>void 0),l?.stop(),l=w(t,d,_)},m.onconnectionstatechange=()=>{f||(m.connectionState===`failed`?h(`error`,Error(`[orb-ui/openai-realtime] WebRTC connection failed.`)):(m.connectionState===`disconnected`||m.connectionState===`closed`)&&h(`idle`))};let y=m.createDataChannel(`oai-events`);a=y,y.onopen=()=>h(`listening`),y.onmessage=e=>{try{E(JSON.parse(String(e.data)))}catch{}},y.onerror=e=>h(`error`,e),y.onclose=()=>{f||h(`idle`)};let b=await m.createOffer();await m.setLocalDescription(b);let x=await r(t.callsUrl??v,{method:`POST`,body:b.sdp,headers:{Authorization:`Bearer ${p}`,"Content-Type":`application/sdp`}});if(!x.ok){let e=await x.text();throw Error(`[orb-ui/openai-realtime] Session negotiation failed (${x.status}): ${e}`)}await m.setRemoteDescription({type:`answer`,sdp:await x.text()})}catch(e){throw await D(!1),h(`error`,e),e}}},async stop(){await D(!0)}}}var D=16e3,O={noiseFloor:.003,gain:4,exponent:.8,attack:.3,release:.1};function k(e){let t=0;for(let n of e)t+=n*n;return Math.sqrt(t/e.length)}function A(e){return Math.min(1,Math.max(0,(k(e)*4)**.8))}function j(e,t,n){if(t===n)return e;let r=t/n,i=Math.max(1,Math.round(e.length/r)),a=new Float32Array(i);for(let t=0;t<i;t+=1){let n=t*r,i=Math.floor(n),o=Math.min(e.length-1,i+1),s=n-i;a[t]=e[i]*(1-s)+e[o]*s}return a}function M(e){let t=new Uint8Array(e.length*2),n=new DataView(t.buffer);e.forEach((e,t)=>{let r=Math.max(-1,Math.min(1,e));n.setInt16(t*2,r<0?r*32768:r*32767,!0)});let r=``;return t.forEach(e=>{r+=String.fromCharCode(e)}),btoa(r)}function N(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e+=1)n[e]=t.charCodeAt(e);let r=new DataView(n.buffer),i=new Float32Array(Math.floor(n.length/2));for(let e=0;e<i.length;e+=1)i[e]=r.getInt16(e*2,!0)/32768;return i}function P(e){let t=e?.match(/rate=(\d+)/i);return t?Number(t[1]):24e3}function F(t){let n=new Set,r=new Set,i={state:`idle`,volume:0,inputVolume:0,outputVolume:0},a=null,o=null,s=null,c=null,l=null,u=null,d=null,f=null,p=null,m=!1,h=0,g=0,_=!1,v=!1;function y(){return t.activityDetection!==`server`}function b(){let e=typeof t.outputVolumeCalibration==`function`?t.outputVolumeCalibration():t.outputVolumeCalibration;return{...O,...e}}function x(e){i=e,n.forEach(t=>t(e))}function S(e,t){(i.state!==e||t!==void 0)&&(e!==`speaking`&&(g=0),x({state:e,volume:0,inputVolume:0,outputVolume:0,...t===void 0?{}:{error:t}}))}function C(){p&&=(clearTimeout(p),null)}function w(e){e>(t.speechThreshold??.04)?(!m&&y()&&a?.sendRealtimeInput({activityStart:{}}),m=!0,C(),i.state!==`listening`&&S(`listening`)):m&&!p&&(p=setTimeout(()=>{p=null,m=!1,y()&&a?.sendRealtimeInput({activityEnd:{}}),i.state===`listening`&&S(`thinking`)},t.speechEndDelayMs??500)),i.state===`listening`&&x({...i,volume:e,inputVolume:e,outputVolume:0})}function T(){r.forEach(e=>{try{e.stop()}catch{}e.disconnect()}),r.clear(),h=s?.currentTime??0}function E(){_&&r.size===0&&(_=!1,S(`listening`))}function F(e){if(!s||!d||!e.data)return;let t=N(e.data),n=s.createBuffer(1,t.length,P(e.mimeType));n.getChannelData(0).set(t);let a=s.createBufferSource();a.buffer=n,a.connect(d);let o=Math.max(s.currentTime,h);h=o+n.duration,r.add(a),a.onended=()=>{r.delete(a),a.disconnect(),E()},a.start(o),i.state!==`speaking`&&S(`speaking`)}function I(e){e.setupComplete&&S(`listening`);let t=e.serverContent;if(!t)return;if(t.interrupted){T(),_=!1,S(`listening`);return}let n=t.modelTurn?.parts?.filter(e=>e.inlineData?.data)??[];t.modelTurn&&n.length===0&&i.state!==`speaking`&&S(`thinking`),n.forEach(e=>F(e.inlineData)),t.waitingForInput&&(T(),S(`listening`)),(t.turnComplete||t.generationComplete)&&(_=!0,E())}function L(){if(!d)return;let n=new Float32Array(d.fftSize);f=setInterval(()=>{if(!d||i.state!==`speaking`)return;d.getFloatTimeDomainData(n);let r=e.r(k(n),g,b);g=r.normalized,t.onOutputVolumeSample?.(r);let a=r.normalized;x({...i,volume:a,inputVolume:0,outputVolume:a})},33)}function R(){s&&o&&a&&(c=s.createMediaStreamSource(o),l=s.createScriptProcessor(4096,1,1),u=s.createGain(),u.gain.value=0,c.connect(l),l.connect(u),u.connect(s.destination),l.onaudioprocess=e=>{if(!a||v)return;let n=e.inputBuffer.getChannelData(0);w(A(n));let r=j(n,s?.sampleRate??D,t.inputSampleRate??D);a.sendRealtimeInput({audio:{data:M(r),mimeType:`audio/pcm;rate=${t.inputSampleRate??D}`}})})}async function z(e){v=!0,C(),f&&clearInterval(f),f=null,T(),l?.disconnect(),c?.disconnect(),u?.disconnect(),d?.disconnect(),l=null,c=null,u=null,d=null,o?.getTracks().forEach(e=>e.stop()),o=null,s&&s.state!==`closed`&&await s.close(),s=null,a=null,m=!1,_=!1,g=0,e&&S(`idle`),v=!1}return{subscribe(e){return n.add(e),e(i),()=>n.delete(e)},async start(){if(!a){S(`connecting`),v=!1;try{let e=t.getUserMedia??(e=>navigator.mediaDevices.getUserMedia(e)),n=t.createAudioContext??(()=>new window.AudioContext);o=await e(t.mediaStreamConstraints??{audio:!0}),s=n(),s.state===`suspended`&&await s.resume(),d=s.createAnalyser(),d.fftSize=512,d.smoothingTimeConstant=.25,d.connect(s.destination),L(),a=await t.connect({onopen:()=>S(`listening`),onmessage:I,onerror:e=>S(`error`,e),onclose:()=>{v||S(`idle`)}}),R(),S(`listening`)}catch(e){throw a?.close(),await z(!1),S(`error`,e),e}}},async stop(){v=!0;try{m&&y()&&a?.sendRealtimeInput({activityEnd:{}}),a?.sendRealtimeInput({audioStreamEnd:!0})}catch{}a?.close(),await z(!0)}}}exports.DEFAULT_OUTPUT_VOLUME_CALIBRATION=e.n,exports.createElevenLabsAdapter=i,exports.createGeminiLiveAdapter=F,exports.createLiveKitAdapter=e.t,exports.createOpenAIRealtimeAdapter=E,exports.createPipecatAdapter=_,exports.createVapiAdapter=r;