@animated-waffle/client 0.5.1 → 0.5.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.3
4
+
5
+ - Observe decoded audio activity when RTC omits its optional audio level, so first-playback estimates still work with those senders.
6
+ - Add content-free session diagnostics and same-clock first-playback estimates for paired Avatar audio. Preserve missing, muted and interrupted observations as unavailable.
7
+
8
+
9
+ ## 0.5.2
10
+
11
+ - Upgrade the browser LiveKit client to 2.22.4. The Waffle session and transcript APIs are unchanged.
12
+
3
13
  ## 0.5.1
4
14
 
5
15
  - Expose protected lifecycle state and event methods for the native SDK adapter. Browser behavior is unchanged.
package/README.md CHANGED
@@ -4,7 +4,9 @@ Framework-independent browser SDK for embedding a published Narya talking
4
4
  Agent: connection lifecycle, microphone input, text input, state, and
5
5
  transcripts. React applications should use
6
6
  [`@animated-waffle/react`](../waffle-react), which re-exports this entire API
7
- and adds the Avatar renderer.
7
+ and adds the Avatar renderer. For plain JavaScript or another browser framework, use
8
+ [`@animated-waffle/web`](../waffle-web) and `mountWaffleAvatar`. This package is
9
+ headless; it does not render a character.
8
10
 
9
11
  Install the current npm release without copying a mutable registry version:
10
12
 
@@ -169,3 +171,22 @@ For an application-owned conversation, use `application: true` with a
169
171
  `WaffleServer.startApplicationSession`. The backend retains the separate control
170
172
  grant; the browser never executes tools or forwards privileged callbacks.
171
173
  See the [application-session contract](../../docs/public/sdk.md#application-owned-tools-and-conversation-state).
174
+
175
+ ## Development diagnostics
176
+
177
+ Read `client.diagnostics` and subscribe with `client.on('diagnostics', render)`.
178
+ React returns the same snapshot as `waffle.diagnostics`. The React `useWaffle` and
179
+ web `mountWaffleAvatar` adapters show a collapsed floating diagnostics panel in
180
+ development builds; production defaults off. Their `diagnostics` option overrides
181
+ visibility. For an inline view, use `WaffleDiagnosticsPanel` or
182
+ `mountWaffleDiagnostics(host, client)` and disable the automatic overlay.
183
+ The headless client never adds UI. Mounting a panel never starts a connection or microphone.
184
+
185
+ The panel shows connection/media/Avatar state, connection phases, latest client
186
+ input-end-to-playback estimate, safe errors and the current Session Studio link.
187
+ Updated workers additionally report ASR, LLM, TTS and worker-output timings using
188
+ Studio's reducer. Client and server rows have separate correlation IDs and clocks;
189
+ do not sum the stages or treat a worker output as browser playback. Missing data
190
+ remains unavailable. Voice input activity and RTC/paired output timing are estimates,
191
+ not calibrated acoustic measurements. Snapshots exclude text, audio, tokens and raw
192
+ error messages. The panel is optional; existing timing telemetry is independent of it.
@@ -0,0 +1 @@
1
+ var o=new WeakMap,i=new WeakMap;function s(e){return i.get(e)??!1}var l=new WeakMap;function f(e){return l.get(e)??null}function v(e,n,t){let a=o.get(e)??new Map,r=a.get(n)??new Set;return r.add(t),a.set(n,r),o.set(e,a),()=>r.delete(t)}function c(e,n,t){n==="motionMode"&&i.set(e,t),n==="presentation"&&l.set(e,t),o.get(e)?.get(n)?.forEach(a=>{a(t)})}export{s as a,f as b,v as c,c as d};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,90 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ /** Durations from one worker clock. Never subtract these from client timestamps. */
4
+ declare const sdkServerDiagnosticsSchema: z.ZodObject<{
5
+ version: z.ZodLiteral<1>;
6
+ sessionId: z.ZodString;
7
+ traceId: z.ZodString;
8
+ inputModality: z.ZodEnum<{
9
+ audio: "audio";
10
+ text: "text";
11
+ }>;
12
+ inputToWorkerAudio: z.ZodObject<{
13
+ milliseconds: z.ZodNullable<z.ZodNumber>;
14
+ unavailableReason: z.ZodNullable<z.ZodString>;
15
+ }, z.core.$strict>;
16
+ speechEndToAsrFinal: z.ZodObject<{
17
+ milliseconds: z.ZodNullable<z.ZodNumber>;
18
+ unavailableReason: z.ZodNullable<z.ZodString>;
19
+ }, z.core.$strict>;
20
+ llmFirstToken: z.ZodObject<{
21
+ milliseconds: z.ZodNullable<z.ZodNumber>;
22
+ unavailableReason: z.ZodNullable<z.ZodString>;
23
+ }, z.core.$strict>;
24
+ ttsFirstAudio: z.ZodObject<{
25
+ milliseconds: z.ZodNullable<z.ZodNumber>;
26
+ unavailableReason: z.ZodNullable<z.ZodString>;
27
+ }, z.core.$strict>;
28
+ timeline: z.ZodOptional<z.ZodObject<{
29
+ anchor: z.ZodEnum<{
30
+ speech_end: "speech_end";
31
+ input_accepted: "input_accepted";
32
+ }>;
33
+ firstAudioMs: z.ZodNullable<z.ZodNumber>;
34
+ observedMs: z.ZodNumber;
35
+ complete: z.ZodBoolean;
36
+ omittedSpans: z.ZodNumber;
37
+ spans: z.ZodArray<z.ZodObject<{
38
+ kind: z.ZodEnum<{
39
+ asr: "asr";
40
+ llm: "llm";
41
+ tts: "tts";
42
+ }>;
43
+ startMs: z.ZodNumber;
44
+ firstOutputMs: z.ZodNullable<z.ZodNumber>;
45
+ endMs: z.ZodNullable<z.ZodNumber>;
46
+ }, z.core.$strict>>;
47
+ }, z.core.$strict>>;
48
+ }, z.core.$strict>;
49
+ type SdkServerDiagnostics = z.infer<typeof sdkServerDiagnosticsSchema>;
50
+
51
+ declare const audioTimingUnsupportedReasonSchema: z.ZodEnum<{
52
+ native_playback_unavailable: "native_playback_unavailable";
53
+ custom_playback: "custom_playback";
54
+ avatar_playback_unavailable: "avatar_playback_unavailable";
55
+ audio_observer_unavailable: "audio_observer_unavailable";
56
+ }>;
57
+ type AudioTimingUnsupportedReason = z.infer<typeof audioTimingUnsupportedReasonSchema>;
58
+
59
+ interface WaffleDiagnostics {
60
+ readonly connectionState: WaffleConnectionState;
61
+ readonly sessionId: string | null;
62
+ readonly agentStudioUrl: string | null;
63
+ readonly inputMode: WaffleInputMode;
64
+ readonly microphone: 'off' | 'disabled' | 'requesting' | 'on' | 'error';
65
+ readonly playback: 'idle' | 'speaking' | 'muted' | 'blocked' | 'custom';
66
+ readonly motion: 'off' | 'waiting' | 'active';
67
+ readonly avatar: 'idle' | 'downloading' | 'verifying' | 'parsing' | 'ready' | 'error';
68
+ readonly connectionMs: number | null;
69
+ readonly tokenMs: number | null;
70
+ readonly sessionMs: number | null;
71
+ readonly transportMs: number | null;
72
+ readonly agentReadyMs: number | null;
73
+ readonly server: SdkServerDiagnostics | null;
74
+ readonly turn: {
75
+ readonly id: string;
76
+ readonly kind: 'voice';
77
+ readonly responseId: string | null;
78
+ readonly firstPlaybackMs: number | null;
79
+ readonly source: 'rtc_playout_estimate' | 'paired_audio_output_estimate' | null;
80
+ readonly unavailableReason: string | null;
81
+ } | null;
82
+ readonly error: {
83
+ readonly stage: string;
84
+ readonly code: string | null;
85
+ } | null;
86
+ }
87
+
3
88
  declare const characterCardSchema: z.ZodObject<{
4
89
  spec: z.ZodLiteral<"chara_card_v3">;
5
90
  spec_version: z.ZodLiteral<"3.0">;
@@ -127,6 +212,7 @@ interface WaffleTranscriptProgress {
127
212
  complete: boolean;
128
213
  }
129
214
  interface WaffleClientEvents {
215
+ diagnostics: WaffleDiagnostics;
130
216
  connectionState: WaffleConnectionState;
131
217
  /** @deprecated Subscribe to connectionState. */
132
218
  status: WaffleStatus;
@@ -281,6 +367,9 @@ declare class WaffleError extends Error {
281
367
  constructor(message: string, status?: number | undefined, code?: string | undefined, stage?: WaffleErrorStage, options?: ErrorOptions);
282
368
  }
283
369
  declare class WaffleClient implements WaffleSessionClient {
370
+ private connectionStartedAt;
371
+ private readonly diagnosticState;
372
+ get diagnostics(): WaffleDiagnostics;
284
373
  private room;
285
374
  private readonly listeners;
286
375
  private readonly options;
@@ -293,6 +382,9 @@ declare class WaffleClient implements WaffleSessionClient {
293
382
  private requestAbort;
294
383
  private readyWaiter;
295
384
  private audio;
385
+ private audioTiming;
386
+ private audioTimingPending;
387
+ private readonly audioTimingClockId;
296
388
  private audioMuted;
297
389
  private motionSync;
298
390
  private motionAgentIdentity;
@@ -320,12 +412,14 @@ declare class WaffleClient implements WaffleSessionClient {
320
412
  constructor(options: WaffleClientOptions);
321
413
  private createRoom;
322
414
  get status(): WaffleStatus;
415
+ protected audioTimingUnavailableReason(): AudioTimingUnsupportedReason;
323
416
  get connectionState(): WaffleConnectionState;
324
417
  get session(): WaffleSession | null;
325
418
  get remoteAudioTrack(): MediaStreamTrack | null;
326
419
  get muted(): boolean;
327
420
  set muted(muted: boolean);
328
421
  on<K extends keyof WaffleClientEvents>(event: K, listener: Listener<K>): () => void;
422
+ private prepareAudioTiming;
329
423
  connect(options?: WaffleConnectOptions): Promise<WaffleSession>;
330
424
  disconnect(): Promise<void>;
331
425
  sendText(text: string, options?: WaffleSendTextOptions): Promise<void>;
@@ -371,4 +465,4 @@ declare class WaffleClient implements WaffleSessionClient {
371
465
  protected emit<K extends keyof WaffleClientEvents>(event: K, payload: WaffleClientEvents[K]): void;
372
466
  }
373
467
 
374
- export { type CharacterCard, type PushToTalkControlMethod, WaffleClient, type WaffleClientEvents, type WaffleClientOptions, type WaffleConnectOptions, type WaffleConnectionState, type WaffleDecline, type WaffleDeveloperFeedbackAction, type WaffleDeveloperFeedbackAvailability, type WaffleDeveloperFeedbackDimension, type WaffleDeveloperFeedbackGrant, type WaffleDeveloperFeedbackGrantProvider, type WaffleDeveloperFeedbackGrantRequest, type WaffleDeveloperFeedbackInput, type WaffleDeveloperFeedbackState, WaffleError, type WaffleErrorStage, type WaffleInputMode, type WaffleSendTextOptions, type WaffleSession, type WaffleSessionClient, type WaffleSessionOverrides, type WaffleSessionStartInput, type WaffleSessionStartOptions, type WaffleSessionStarter, type WaffleSessionToken, type WaffleStatus, type WaffleTranscript, type WaffleTranscriptProgress };
468
+ export { type CharacterCard, type PushToTalkControlMethod, WaffleClient, type WaffleClientEvents, type WaffleClientOptions, type WaffleConnectOptions, type WaffleConnectionState, type WaffleDecline, type WaffleDeveloperFeedbackAction, type WaffleDeveloperFeedbackAvailability, type WaffleDeveloperFeedbackDimension, type WaffleDeveloperFeedbackGrant, type WaffleDeveloperFeedbackGrantProvider, type WaffleDeveloperFeedbackGrantRequest, type WaffleDeveloperFeedbackInput, type WaffleDeveloperFeedbackState, type WaffleDiagnostics, WaffleError, type WaffleErrorStage, type WaffleInputMode, type WaffleSendTextOptions, type WaffleSession, type WaffleSessionClient, type WaffleSessionOverrides, type WaffleSessionStartInput, type WaffleSessionStartOptions, type WaffleSessionStarter, type WaffleSessionToken, type WaffleStatus, type WaffleTranscript, type WaffleTranscriptProgress };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import{b as x}from"./chunk-4OFU2T5E.js";import{z as f}from"zod";var ne=["pelvis","left_hip","right_hip","spine1","left_knee","right_knee","spine2","left_ankle","right_ankle","spine3","left_foot","right_foot","neck","left_collar","right_collar","head","left_shoulder","right_shoulder","left_elbow","right_elbow","left_wrist","right_wrist","jaw","left_eye","right_eye",...["left","right"].flatMap(s=>["index","middle","pinky","ring","thumb"].flatMap(e=>[1,2,3].map(t=>`${s}_${e}${t}`)))],ie=["browDownLeft","browDownRight","browInnerUp","browOuterUpLeft","browOuterUpRight","cheekPuff","cheekSquintLeft","cheekSquintRight","eyeBlinkLeft","eyeBlinkRight","eyeLookDownLeft","eyeLookDownRight","eyeLookInLeft","eyeLookInRight","eyeLookOutLeft","eyeLookOutRight","eyeLookUpLeft","eyeLookUpRight","eyeSquintLeft","eyeSquintRight","eyeWideLeft","eyeWideRight","jawForward","jawLeft","jawOpen","jawRight","mouthClose","mouthDimpleLeft","mouthDimpleRight","mouthFrownLeft","mouthFrownRight","mouthFunnel","mouthLeft","mouthLowerDownLeft","mouthLowerDownRight","mouthPressLeft","mouthPressRight","mouthPucker","mouthRight","mouthRollLower","mouthRollUpper","mouthShrugLower","mouthShrugUpper","mouthSmileLeft","mouthSmileRight","mouthStretchLeft","mouthStretchRight","mouthUpperUpLeft","mouthUpperUpRight","noseSneerLeft","noseSneerRight","tongueOut"];var q=ne.length*4,V=q+3,O=V+ie.length;var W="fused-motion-paired.v5",oe="animated-waffle.avatar-motion.frames",re="avatar.motion.configure",se="avatar.motion.cancel",ae="avatar.motion.playback",le="avatar.motion.interrupt",Ze=f.object({}).strict(),de="avatar.motion.caption",Y=15e3,B=30,Ae=12,Ie=8*1024,Z=60,ce=8*1024*1024,pe=1024,_e=1e4;function U(s,e){if(!Number.isSafeInteger(s)||s<0||!Number.isInteger(e)||e<8e3||e>48e3)throw new Error("Invalid motion sample grid");let t=s*e/B,n=Math.floor(t),i=t-n===.5?n+n%2:Math.round(t);if(!Number.isSafeInteger(i))throw new Error("Invalid motion sample grid");return i}var Pe=f.object({id:f.string().uuid(),start:f.number().int().nonnegative(),end:f.number().int().nonnegative().optional(),cancelled:f.literal(!0).optional()}).strict(),Me=f.object({protocol:f.literal(W),id:f.string().uuid(),seq:f.number().int().nonnegative(),sampleRate:f.number().int().min(8e3).max(48e3),frameStart:f.number().int().nonnegative(),frameCount:f.number().int().nonnegative().max(Ae),replies:f.array(Pe).max(16).refine(s=>new Set(s.map(e=>e.id)).size===s.length,"A reply may appear only once per packet")}).strict(),ue=f.object({id:f.string().uuid(),reason:f.enum(["interrupted","connection_closed"]).optional()}).strict(),et=f.object({id:f.string().uuid(),receivedSeq:f.number().int().min(-1).max(4294967295),missingSeq:f.number().int().nonnegative().max(4294967295).optional(),sample:f.number().int().nonnegative(),state:f.enum(["playing","buffering","stopped"]),replies:f.array(f.object({id:f.string().uuid(),playedSamples:f.number().int().nonnegative(),state:f.enum(["playing","finished","interrupted"])}).strict()).max(16).refine(s=>new Set(s.map(e=>e.id)).size===s.length,"A reply may have only one playback state per report")}).strict().superRefine((s,e)=>{s.missingSeq!==void 0&&s.missingSeq!==s.receivedSeq+1&&e.addIssue({code:"custom",path:["missingSeq"],message:"Missing packet must immediately follow the contiguous receipt"})}),he=f.object({id:f.string().uuid(),replyId:f.string().uuid(),segmentId:f.string().max(128),start:f.number().int().nonnegative(),end:f.number().int().nonnegative().optional(),text:f.string().max(4096)}).strict().superRefine((s,e)=>{s.end!==void 0&&s.end<s.start&&e.addIssue({code:"custom",path:["end"],message:"Caption end precedes its start"}),new TextEncoder().encode(JSON.stringify(s)).byteLength>Y&&e.addIssue({code:"custom",message:"Caption packet exceeds its size limit"})});function fe(s){if(s.byteLength>Ie)throw new Error("Motion packet exceeds its size limit");let e=new DataView(s.buffer,s.byteOffset,s.byteLength);if(s.byteLength<4)throw new Error("Motion chunk is truncated");let t=e.getUint32(0,!0);if(t===0||4+t>s.byteLength)throw new Error("Motion chunk header is invalid");let n=Me.parse(JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(s.subarray(4,4+t)))),i=n.frameCount*O,o=U(n.frameStart+n.frameCount,n.sampleRate)-U(n.frameStart,n.sampleRate),r=4+t+i*2;if(s.byteLength!==r+o*2)throw new Error("Motion chunk payload does not match its frame and sample intervals");let l=new Float32Array(i);for(let a=0;a<i;a++){let p=a%O,g=p>=q&&p<V?_e:32767;l[a]=e.getInt16(4+t+a*2,!0)/g}for(let a=0;a<i;a+=O)for(let p=a;p<a+q;p+=4)if(Math.abs(Math.hypot(l[p],l[p+1],l[p+2],l[p+3])-1)>.01)throw new Error("Motion quaternion is not normalized");let c=new Int16Array(o);for(let a=0;a<o;a++)c[a]=e.getInt16(r+a*2,!0);return{...n,frames:l,audio:c}}function Ee(s,e=60,t=.1){let n="",i=0,o=0,r=0,l=0,c=!0,a=!0,p=[],g=new Map,v=new Set,y=m=>{v.add(m),v.size>64&&v.delete(v.values().next().value)},b=()=>o/s,u=m=>{m>b()&&(o=m*s,l++)},k=()=>{for(;p.length&&p[0].start+p[0].audio.length<=Math.floor(b());)p.shift();for(let[m,S]of g)S.end!==void 0&&S.start+S.end<b()-i&&g.delete(m)},M=()=>[...g.values()].sort((m,S)=>m.start-S.start),_=()=>{for(let m of M()){let S=b(),I=m.end===void 0?1/0:m.start+m.end;if(!(I<=S)){if((m.cancelled||v.has(m.id))&&S>=m.start){if(!Number.isFinite(I)||I>r)return!1;u(I);continue}S<m.start&&m.start<r&&u(m.start);break}}return k(),!0};return{reset(m,S){n=m,i=S,o=0,r=0,l++,c=!0,a=!1,p=[],g.clear(),v.clear()},push(m,S,I){if(a)throw new Error("paired_player_stopped");if(m!==r)throw new Error("paired_audio_gap");if(r+S.length-b()>e*i)throw new Error("paired_buffer_overflow");for(let h of I){let w=g.get(h.id);if(w&&(w.start!==h.start||w.end!==void 0&&h.end!==void 0&&w.end!==h.end))throw new Error("paired_reply_changed");g.set(h.id,{...w,...h}),h.cancelled&&y(h.id)}if(g.size>64)throw new Error("paired_reply_overflow");S.length&&p.push({start:m,audio:S}),r+=S.length},cancel(m){if(m)y(m);else for(let S of g.values())y(S.id);c=!0},stop(){a=!0,p=[],g.clear(),c=!0},render(m){if(m.fill(0),a||!n)return{id:n,start:b(),sample:b(),epoch:l,state:"stopped",rendered:0};let S=_(),I=b(),h=l,w=M().find(P=>P.start<=I&&(P.end===void 0||P.start+P.end>I)),$=w?.end!==void 0&&w.start+w.end<=r;if(c&&(r-I>=i*t||$)&&(c=!1),!S||c)return{id:n,start:I,sample:I,epoch:h,state:"buffering",rendered:0};let Q=0,D=0,ke=M().find(P=>(P.cancelled||v.has(P.id))&&(P.end===void 0||P.start+P.end>I))?.start??1/0;for(let P=0;P<m.length;P++){if(b()>=ke){c=!0;break}let G=Math.floor(b()),ee=b()-G;for(;D<p.length&&G>=p[D].start+p[D].audio.length;)D++;let L=p[D];if(!L||G<L.start||b()+i/s>r){c=!0;break}let J=G-L.start,X=L.audio[J]/32768,te=J+1<L.audio.length?L.audio[J+1]:p[D+1]?.audio[0],Te=ee>0&&te!==void 0?te/32768:X;m[P]=X+(Te-X)*ee,o+=i,Q++}return k(),{id:n,start:I,sample:b(),epoch:h,state:c?"buffering":"playing",rendered:Q}},position:b}}var me=`
2
- const createEngine = ${Ee.toString()};
1
+ import{c as ae,d as N}from"./chunk-QY6J2GDU.js";import{z as A}from"zod";var he="animated-waffle.diagnostics.v1",H=A.object({milliseconds:A.number().finite().nonnegative().max(864e5).nullable(),unavailableReason:A.string().min(1).max(80).nullable()}).strict(),$=A.number().finite().min(-864e5).max(864e5),Le=A.object({kind:A.enum(["asr","llm","tts"]),startMs:$,firstOutputMs:$.nullable(),endMs:$.nullable()}).strict().refine(s=>(s.firstOutputMs===null||s.firstOutputMs>=s.startMs)&&(s.endMs===null||s.endMs>=(s.firstOutputMs??s.startMs))),qe=A.object({anchor:A.enum(["speech_end","input_accepted"]),firstAudioMs:A.number().finite().nonnegative().max(864e5).nullable(),observedMs:$,complete:A.boolean(),omittedSpans:A.number().int().nonnegative().max(128),spans:A.array(Le).max(16)}).strict().refine(s=>s.observedMs>=(s.firstAudioMs??0)&&s.spans.every(e=>s.observedMs>=Math.max(e.startMs,e.firstOutputMs??e.startMs,e.endMs??e.startMs))),fe=A.object({version:A.literal(1),sessionId:A.string().uuid(),traceId:A.string().uuid(),inputModality:A.enum(["audio","text"]),inputToWorkerAudio:H,speechEndToAsrFinal:H,llmFirstToken:H,ttsFirstAudio:H,timeline:qe.optional()}).strict();var X=class{constructor(e){this.notify=e;this.value=this.empty()}notify;value;started=0;inputEnded=null;empty(){return{connectionState:"disconnected",sessionId:null,agentStudioUrl:null,inputMode:"push-to-talk",microphone:"off",playback:"idle",motion:"off",avatar:"idle",connectionMs:null,tokenMs:null,sessionMs:null,transportMs:null,agentReadyMs:null,server:null,turn:null,error:null}}get snapshot(){return this.value}update(e){Object.entries(e).every(([t,n])=>this.value[t]===n)||(this.value=Object.freeze({...this.value,...e}),this.notify(this.value))}start(e,t,n,i){this.started=performance.now(),this.inputEnded=null,this.update({...this.empty(),avatar:this.value.avatar,connectionState:"connecting",inputMode:e,microphone:t?"off":"disabled",playback:n?"idle":"custom",motion:i?"waiting":"off"})}connected(){this.update({connectionMs:performance.now()-this.started})}session(e){e&&this.update({sessionId:e.id,agentStudioUrl:e.agentStudioUrl??null})}failure(e){this.update({error:Object.freeze({stage:e.stage,code:e.code??null}),...e.stage==="audio"?e.code==="microphone_unavailable"||["requesting","error"].includes(this.value.microphone)?{microphone:"error"}:{playback:"blocked"}:{}})}audio(e){e.kind==="speech"?(this.inputEnded=e.endedMs,this.update({turn:Object.freeze({id:e.turnId,kind:"voice",responseId:null,firstPlaybackMs:null,source:null,unavailableReason:e.endedMs===null?"input_in_progress":"awaiting_playback"})})):this.value.turn?.id===e.turnId&&(e.kind==="unavailable"?this.update({turn:Object.freeze({...this.value.turn,firstPlaybackMs:null,unavailableReason:e.reason})}):this.inputEnded!==null&&e.playbackMs>=this.inputEnded&&this.update({turn:Object.freeze({...this.value.turn,responseId:e.responseId,firstPlaybackMs:e.playbackMs-this.inputEnded,source:e.source,unavailableReason:null})}))}};import{z as m}from"zod";var me=["pelvis","left_hip","right_hip","spine1","left_knee","right_knee","spine2","left_ankle","right_ankle","spine3","left_foot","right_foot","neck","left_collar","right_collar","head","left_shoulder","right_shoulder","left_elbow","right_elbow","left_wrist","right_wrist","jaw","left_eye","right_eye",...["left","right"].flatMap(s=>["index","middle","pinky","ring","thumb"].flatMap(e=>[1,2,3].map(t=>`${s}_${e}${t}`)))],ge=["browDownLeft","browDownRight","browInnerUp","browOuterUpLeft","browOuterUpRight","cheekPuff","cheekSquintLeft","cheekSquintRight","eyeBlinkLeft","eyeBlinkRight","eyeLookDownLeft","eyeLookDownRight","eyeLookInLeft","eyeLookInRight","eyeLookOutLeft","eyeLookOutRight","eyeLookUpLeft","eyeLookUpRight","eyeSquintLeft","eyeSquintRight","eyeWideLeft","eyeWideRight","jawForward","jawLeft","jawOpen","jawRight","mouthClose","mouthDimpleLeft","mouthDimpleRight","mouthFrownLeft","mouthFrownRight","mouthFunnel","mouthLeft","mouthLowerDownLeft","mouthLowerDownRight","mouthPressLeft","mouthPressRight","mouthPucker","mouthRight","mouthRollLower","mouthRollUpper","mouthShrugLower","mouthShrugUpper","mouthSmileLeft","mouthSmileRight","mouthStretchLeft","mouthStretchRight","mouthUpperUpLeft","mouthUpperUpRight","noseSneerLeft","noseSneerRight","tongueOut"];var K=me.length*4,Y=K+3,U=Y+ge.length;var V="fused-motion-paired.v5",ve="animated-waffle.avatar-motion.frames",ye="avatar.motion.configure",be="avatar.motion.cancel",ke="avatar.motion.playback",Se="avatar.motion.interrupt",kt=m.object({}).strict(),we="avatar.motion.caption",le=15e3,Z=30,je=12,Ve=8*1024,de=60,Te=8*1024*1024,Ie=1024,Ge=1e4;function B(s,e){if(!Number.isSafeInteger(s)||s<0||!Number.isInteger(e)||e<8e3||e>48e3)throw new Error("Invalid motion sample grid");let t=s*e/Z,n=Math.floor(t),i=t-n===.5?n+n%2:Math.round(t);if(!Number.isSafeInteger(i))throw new Error("Invalid motion sample grid");return i}var Be=m.object({id:m.string().uuid(),start:m.number().int().nonnegative(),end:m.number().int().nonnegative().optional(),cancelled:m.literal(!0).optional()}).strict(),ze=m.object({protocol:m.literal(V),id:m.string().uuid(),seq:m.number().int().nonnegative(),sampleRate:m.number().int().min(8e3).max(48e3),frameStart:m.number().int().nonnegative(),frameCount:m.number().int().nonnegative().max(je),replies:m.array(Be).max(16).refine(s=>new Set(s.map(e=>e.id)).size===s.length,"A reply may appear only once per packet")}).strict(),Ae=m.object({id:m.string().uuid(),reason:m.enum(["interrupted","connection_closed"]).optional()}).strict(),wt=m.object({id:m.string().uuid(),receivedSeq:m.number().int().min(-1).max(4294967295),missingSeq:m.number().int().nonnegative().max(4294967295).optional(),sample:m.number().int().nonnegative(),state:m.enum(["playing","buffering","stopped"]),replies:m.array(m.object({id:m.string().uuid(),playedSamples:m.number().int().nonnegative(),state:m.enum(["playing","finished","interrupted"])}).strict()).max(16).refine(s=>new Set(s.map(e=>e.id)).size===s.length,"A reply may have only one playback state per report")}).strict().superRefine((s,e)=>{s.missingSeq!==void 0&&s.missingSeq!==s.receivedSeq+1&&e.addIssue({code:"custom",path:["missingSeq"],message:"Missing packet must immediately follow the contiguous receipt"})}),_e=m.object({id:m.string().uuid(),replyId:m.string().uuid(),segmentId:m.string().max(128),start:m.number().int().nonnegative(),end:m.number().int().nonnegative().optional(),text:m.string().max(4096)}).strict().superRefine((s,e)=>{s.end!==void 0&&s.end<s.start&&e.addIssue({code:"custom",path:["end"],message:"Caption end precedes its start"}),new TextEncoder().encode(JSON.stringify(s)).byteLength>le&&e.addIssue({code:"custom",message:"Caption packet exceeds its size limit"})});function Me(s){if(s.byteLength>Ve)throw new Error("Motion packet exceeds its size limit");let e=new DataView(s.buffer,s.byteOffset,s.byteLength);if(s.byteLength<4)throw new Error("Motion chunk is truncated");let t=e.getUint32(0,!0);if(t===0||4+t>s.byteLength)throw new Error("Motion chunk header is invalid");let n=ze.parse(JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(s.subarray(4,4+t)))),i=n.frameCount*U,o=B(n.frameStart+n.frameCount,n.sampleRate)-B(n.frameStart,n.sampleRate),r=4+t+i*2;if(s.byteLength!==r+o*2)throw new Error("Motion chunk payload does not match its frame and sample intervals");let l=new Float32Array(i);for(let a=0;a<i;a++){let u=a%U,g=u>=K&&u<Y?Ge:32767;l[a]=e.getInt16(4+t+a*2,!0)/g}for(let a=0;a<i;a+=U)for(let u=a;u<a+K;u+=4)if(Math.abs(Math.hypot(l[u],l[u+1],l[u+2],l[u+3])-1)>.01)throw new Error("Motion quaternion is not normalized");let c=new Int16Array(o);for(let a=0;a<o;a++)c[a]=e.getInt16(r+a*2,!0);return{...n,frames:l,audio:c}}function Ke(s,e=60,t=.1){let n="",i=0,o=0,r=0,l=0,c=!0,a=!0,u=[],g=new Map,k=new Set,f=h=>{k.add(h),k.size>64&&k.delete(k.values().next().value)},y=()=>o/s,p=h=>{h>y()&&(o=h*s,l++)},T=()=>{for(;u.length&&u[0].start+u[0].audio.length<=Math.floor(y());)u.shift();for(let[h,S]of g)S.end!==void 0&&S.start+S.end<y()-i&&g.delete(h)},P=()=>[...g.values()].sort((h,S)=>h.start-S.start),j=()=>{for(let h of P()){let S=y(),I=h.end===void 0?1/0:h.start+h.end;if(!(I<=S)){if((h.cancelled||k.has(h.id))&&S>=h.start){if(!Number.isFinite(I)||I>r)return!1;p(I);continue}S<h.start&&h.start<r&&p(h.start);break}}return T(),!0};return{reset(h,S){n=h,i=S,o=0,r=0,l++,c=!0,a=!1,u=[],g.clear(),k.clear()},push(h,S,I){if(a)throw new Error("paired_player_stopped");if(h!==r)throw new Error("paired_audio_gap");if(r+S.length-y()>e*i)throw new Error("paired_buffer_overflow");for(let E of I){let v=g.get(E.id);if(v&&(v.start!==E.start||v.end!==void 0&&E.end!==void 0&&v.end!==E.end))throw new Error("paired_reply_changed");g.set(E.id,{...v,...E}),E.cancelled&&f(E.id)}if(g.size>64)throw new Error("paired_reply_overflow");S.length&&u.push({start:h,audio:S}),r+=S.length},cancel(h){if(h)f(h);else for(let S of g.values())f(S.id);c=!0},stop(){a=!0,u=[],g.clear(),c=!0},render(h){if(h.fill(0),a||!n)return{id:n,start:y(),sample:y(),epoch:l,state:"stopped",rendered:0};let S=j(),I=y(),E=l,v=P().find(x=>x.start<=I&&(x.end===void 0||x.start+x.end>I)),b=v?.end!==void 0&&v.start+v.end<=r;if(c&&(r-I>=i*t||b)&&(c=!1),!S||c)return{id:n,start:I,sample:I,epoch:E,state:"buffering",rendered:0};let D=0,W=0,De=P().find(x=>(x.cancelled||k.has(x.id))&&(x.end===void 0||x.start+x.end>I))?.start??1/0;for(let x=0;x<h.length;x++){if(y()>=De){c=!0;break}let J=Math.floor(y()),ue=y()-J;for(;W<u.length&&J>=u[W].start+u[W].audio.length;)W++;let G=u[W];if(!G||J<G.start||y()+i/s>r){c=!0;break}let re=J-G.start,se=G.audio[re]/32768,pe=re+1<G.audio.length?G.audio[re+1]:u[W+1]?.audio[0],Ue=ue>0&&pe!==void 0?pe/32768:se;h[x]=se+(Ue-se)*ue,o+=i,D++}return T(),{id:n,start:I,sample:y(),epoch:E,state:c?"buffering":"playing",rendered:D}},position:y}}var Pe=`
2
+ const createEngine = ${Ke.toString()};
3
3
  registerProcessor('waffle-paired-playout', class extends AudioWorkletProcessor {
4
4
  constructor(options) {
5
5
  super(); this.engine = createEngine(sampleRate, options.processorOptions.maxSeconds, options.processorOptions.bufferSeconds);
@@ -21,4 +21,19 @@ registerProcessor('waffle-paired-playout', class extends AudioWorkletProcessor {
21
21
  return true;
22
22
  }
23
23
  });
24
- `;var Ce=15e3,j=class{constructor(e,t=()=>new AudioContext){this.callbacks=e;this.makeContext=t}callbacks;makeContext;context=null;graph=null;ready=null;timeline=null;clocks=[];ended=new Set;captions=new Map;visible=new Map;cancelled=new Set;cancelledSamples=new Map;animation=null;shown=null;muted=!1;failed=!1;disposed=!1;lastProgress="";lastProgressAt=-1/0;replyStates=new Map;snapshot=null;heartbeat=null;prepare(){if(this.ready)return this.ready;let e=this.context=this.makeContext(),t=e.resume();e.onstatechange=()=>{e.state==="closed"&&!this.disposed?this.fail(new Error("paired_audio_closed")):(this.update(!0,!1),this.animate())};let n=URL.createObjectURL(new Blob([me],{type:"text/javascript"}));return this.ready=Promise.all([t,e.audioWorklet.addModule(n)]).then(()=>{if(this.disposed||this.context!==e)return;if(e.state!=="running")throw new Error("paired_audio_suspended");let i=new AudioWorkletNode(e,"waffle-paired-playout",{numberOfInputs:0,numberOfOutputs:1,outputChannelCount:[1],processorOptions:{maxSeconds:Z,bufferSeconds:.1}}),o=e.createGain();o.gain.value=this.muted?0:1,i.port.onmessage=({data:r})=>{if(!(this.disposed||this.failed)){if(r.type==="failed")return this.fail(new Error("paired_worklet_failed"));r.type!=="clock"||r.id!==this.timeline?.id||(this.clocks.push(r),this.clocks=this.clocks.filter(l=>l.quantumEnd>=e.currentTime-2),this.update(!1,!1),this.animate())}},i.onprocessorerror=()=>this.fail(new Error("paired_worklet_failed")),i.connect(o),o.connect(e.destination),this.graph={node:i,gain:o},this.heartbeat=setInterval(()=>{if(this.disposed||this.failed)return;let r=this.timeline?.gapSince;if(r!=null&&performance.now()-r>=Ce){this.fail(new Error(`paired_chunk_gap_timeout: seq ${this.timeline.seq+1}`));return}this.update(!0,!1)},1e3)}).finally(()=>URL.revokeObjectURL(n)),this.ready}async resume(){await this.prepare(),await this.context?.resume()}setMuted(e){this.muted=e,this.graph&&(this.graph.gain.gain.value=e?0:1)}add(e){if(this.disposed||this.failed||this.ended.has(e.id))return;if(!this.graph)throw new Error("paired_audio_not_ready");let t=this.timeline;if(t?.id!==e.id){t&&(this.clear(),this.remember(this.ended,t.id)),t=this.timeline={id:e.id,rate:e.sampleRate,nextFrame:0,seq:-1,sample:0,frames:new Map,replies:new Map,cancelled:new Set,pending:new Map,pendingBytes:0,gapSince:null},this.clocks=[],this.lastProgress="",this.lastProgressAt=-1/0,this.replyStates.clear(),this.snapshot=null,this.visible.clear(),this.show(null),this.graph.node.port.postMessage({type:"reset",id:e.id,sampleRate:e.sampleRate});for(let[c,a]of this.captions)this.ended.has(a.id)&&this.captions.delete(c)}if(e.seq<=t.seq||t.pending.has(e.seq))return;if(e.sampleRate!==t.rate)throw new Error("paired_sample_rate_changed");let n=U(e.frameStart,e.sampleRate),i=U(e.frameStart+e.frameCount,e.sampleRate);if(e.audio.length!==i-n||e.frames.length!==e.frameCount*O)throw new Error("paired_chunk_length");if(i-t.sample>Z*t.rate)throw new Error("paired_buffer_overflow");let o=e.frames.byteLength+e.audio.byteLength+new TextEncoder().encode(JSON.stringify({...e,frames:void 0,audio:void 0})).byteLength;if(t.pending.size>=pe||t.pendingBytes+o>ce)throw new Error("paired_reorder_overflow");t.pending.set(e.seq,{chunk:e,bytes:o}),t.pendingBytes+=o;let r=t.seq,l=t.gapSince!==null;for(;t.pending.has(t.seq+1);){let c=t.pending.get(t.seq+1);this.append(t,c.chunk),t.pending.delete(t.seq),t.pendingBytes-=c.bytes}t.gapSince=t.pending.size?t.gapSince??performance.now():null,this.update(l!==(t.gapSince!==null)||r===-1,!1),this.animate()}append(e,t){if(t.frameStart!==e.nextFrame)throw new Error(`paired_frame_gap: seq ${t.seq}; frame ${e.nextFrame}->${t.frameStart}`);let n=U(t.frameStart,t.sampleRate);for(let o of t.replies){let r=e.replies.get(o.id);if(r&&(r.start!==o.start||r.end!==void 0&&o.end!==void 0&&r.end!==o.end))throw new Error("paired_reply_changed");e.replies.set(o.id,{...r,...o}),(o.cancelled||this.cancelled.has(o.id))&&(e.cancelled.add(o.id),this.cancelledSamples.has(o.id)||this.cancelledSamples.set(o.id,Math.min(o.end??1/0,Math.max(0,Math.floor(e.sample-o.start)))))}if(e.replies.size>64)throw new Error("paired_reply_overflow");for(let o=0;o<t.frameCount;o++)e.frames.set(t.frameStart+o,t.frames.slice(o*O,(o+1)*O));e.nextFrame+=t.frameCount,e.seq=t.seq;let i=Int16Array.from(t.audio);this.graph.node.port.postMessage({type:"push",start:n,audio:i,replies:t.replies},[i.buffer]);for(let o of t.replies)e.cancelled.has(o.id)&&this.graph.node.port.postMessage({type:"cancel",id:o.id})}addCaption(e){if(this.ended.has(e.id)||this.disposed||this.failed)return;let t=`${e.id}:${e.replyId}:${e.segmentId}`,n=this.captions.get(t);if(n&&(n.start!==e.start||n.text!==e.text||n.end!==void 0&&e.end!==void 0&&n.end!==e.end))throw new Error("paired_caption_changed");if(this.captions.size>=128&&!n){for(let i of this.captions.values())if(!(i.replyId===e.replyId||!this.visible.get(i.replyId)?.complete)){for(let[o,r]of this.captions)r.replyId===i.replyId&&this.captions.delete(o);if(this.captions.size<128)break}}if(this.captions.size>=128&&!n)throw new Error("paired_caption_overflow");this.captions.set(t,{...n,...e}),this.animate()}cancel(e){this.update();let t=this.timeline?.replies.get(e);return t?.end!==void 0&&this.timeline.sample>=t.start+t.end&&!this.timeline.cancelled.has(e)?this.snapshot:(this.cancelledSamples.has(e)||this.cancelledSamples.set(e,t?Math.min(t.end??1/0,Math.max(0,Math.floor(this.timeline.sample-t.start))):0),this.cancelledSamples.size>64&&this.cancelledSamples.delete(this.cancelledSamples.keys().next().value),this.remember(this.cancelled,e),this.timeline?.cancelled.add(e),this.graph?.node.port.postMessage({type:"cancel",id:e}),this.show(null),this.update(!0),this.snapshot)}clear(){for(let e of this.timeline?.replies.values()??[])this.cancel(e.id)}fail(e){this.failed||this.disposed||(this.clear(),this.failed=!0,this.heartbeat!==null&&clearInterval(this.heartbeat),this.heartbeat=null,this.timeline?.pending.clear(),this.timeline&&(this.timeline.pendingBytes=0),this.graph?.node.port.postMessage({type:"stop"}),this.show(null),this.update(!0),this.callbacks.fail(e))}dispose(){this.disposed||(this.clear(),this.disposed=!0,this.heartbeat!==null&&clearInterval(this.heartbeat),this.heartbeat=null,this.graph?.node.port.postMessage({type:"stop"}),this.graph&&(this.graph.node.port.onmessage=null,this.graph.node.disconnect(),this.graph.gain.disconnect()),this.graph=null,this.animation!==null&&cancelAnimationFrame(this.animation),this.animation=null,this.context&&(this.context.onstatechange=null,this.context.close().catch(()=>{})),this.context=null,this.timeline=null,this.clocks=[],this.captions.clear(),this.visible.clear(),this.show(null))}audibleClock(){let e=this.context,t=this.timeline;if(!e||!t)return null;if(e.state!=="running")return{sample:t.sample,epoch:this.clocks.at(-1)?.epoch??0,state:this.failed?"stopped":"buffering"};let n=e.getOutputTimestamp?.(),i=typeof n?.performanceTime=="number"&&n.performanceTime>0?Math.max(0,(performance.now()-n.performanceTime)/1e3):0,o=typeof n?.contextTime=="number"&&n.contextTime>0?Math.min(e.currentTime,n.contextTime+i):Math.max(0,e.currentTime-(e.outputLatency||0)-(e.baseLatency||0)),r=[...this.clocks].reverse().find(a=>a.contextStart<=o);if(!r)return{sample:t.sample,epoch:0,state:"buffering"};let l=r.contextEnd>r.contextStart?Math.min(1,Math.max(0,(o-r.contextStart)/(r.contextEnd-r.contextStart))):0,c=r.start+(r.sample-r.start)*l;return{sample:Math.max(t.sample,c),epoch:r.epoch,state:this.failed?"stopped":e.state!=="running"||o>=r.contextEnd?"buffering":"playing"}}update(e=!1,t=!0){let n=this.timeline,i=this.audibleClock();if(!n||!i)return!1;n.sample=i.sample;let o=[];for(let u of n.replies.values()){let k=n.cancelled.has(u.id),M=k?this.cancelledSamples.get(u.id)??0:Math.min(u.end??1/0,Math.max(0,Math.floor(n.sample-u.start)));if(n.sample<u.start&&!k||M===0&&!k&&u.end!==0)continue;let _=k?"interrupted":u.end!==void 0&&M>=u.end?"finished":"playing";o.push({id:u.id,playedSamples:M,state:_}),this.caption(u,M,_!=="playing",_==="finished")}let r=[...n.replies.values()].find(u=>u.start<=n.sample&&(u.end===void 0||n.sample<u.start+u.end)),l=Math.min(n.nextFrame-1,Math.floor(n.sample*B/n.rate)),c=n.frames.get(l);t&&!this.failed&&!(r&&n.cancelled.has(r.id))&&c&&this.show({id:`${n.id}:${i.epoch}`,index:l,values:c});let a={id:n.id,sample:Math.floor(n.sample),receivedSeq:n.seq,...n.pending.size?{missingSeq:n.seq+1}:{},state:this.failed?"stopped":i.state,replies:o.slice(-16)};this.snapshot=a;let p=a.replies.filter(u=>u.state==="finished"&&u.playedSamples>0&&!this.replyStates.has(u.id));if(p.length){let u=new Set(p.map(k=>k.id));this.callbacks.progress({...a,replies:a.replies.map(k=>u.has(k.id)?{...k,state:"playing"}:k)});for(let k of p)this.replyStates.set(k.id,"playing")}let g=JSON.stringify(a),v=o.some(u=>this.replyStates.get(u.id)!==u.state);if(e||g!==this.lastProgress&&(v||performance.now()-this.lastProgressAt>=100)){this.lastProgress=g,this.lastProgressAt=performance.now(),this.callbacks.progress(a);for(let u of o)this.replyStates.set(u.id,u.state)}let y=Math.floor(n.sample*B/n.rate)-2;for(let u of n.frames.keys())u<y&&n.frames.delete(u);let b=o.filter(u=>u.state!=="playing").slice(0,-16);for(let u of b){n.replies.delete(u.id),n.cancelled.delete(u.id),this.visible.delete(u.id),this.replyStates.delete(u.id);for(let[k,M]of this.captions)M.replyId===u.id&&this.captions.delete(k)}return!this.disposed&&!this.failed}caption(e,t,n,i){let o=this.timeline,r=[...this.captions.values()].filter(p=>p.id===o.id&&p.replyId===e.id).sort((p,g)=>p.start-g.start);if(!r.length)return;let l=r.map(p=>{let g=Array.from(p.text),v=Math.max(0,t-p.start),y=i?g.length:p.end!==void 0?Math.floor(g.length*Math.min(1,v/Math.max(1,p.end-p.start))):Math.floor(v/o.rate*13);return g.slice(0,y).join("")}).join(""),c=this.visible.get(e.id),a={segmentId:e.id,role:"assistant",text:l.length<(c?.text.length??0)?c.text:l,complete:n};a.text&&(a.text!==c?.text||a.complete!==c.complete)&&(this.visible.set(e.id,a),this.callbacks.caption(a))}animate(){this.animation!==null||this.disposed||this.failed||typeof requestAnimationFrame!="function"||(this.animation=requestAnimationFrame(()=>{this.animation=null,this.update()&&this.animate()}))}show(e){let t=e?`${e.id}:${e.index}`:null;this.shown!==t&&(this.shown=t,this.callbacks.frame(e))}remember(e,t){e.add(t),e.size>64&&e.delete(e.values().next().value)}};import{DataPacket_Kind as xe,ParticipantKind as N,Room as Re,RoomEvent as C,RpcError as Se,Track as K}from"livekit-client";var ge=15e3,Oe=3e4,Fe="https://animated-waffle.narya.ai",z=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,We=/^sha256:[a-f0-9]{64}$/,d=class extends Error{constructor(t,n,i,o="session",r){super(t,r);this.status=n;this.code=i;this.stage=o;this.name="WaffleError"}status;code;stage;rpcMethod;livekitErrorCode},F={APPLICATION_ERROR:1500,CONNECTION_TIMEOUT:1501,RESPONSE_TIMEOUT:1502,RECIPIENT_DISCONNECTED:1503},Ne=new Set([F.CONNECTION_TIMEOUT,F.RESPONSE_TIMEOUT]);function De(s){return s instanceof Se&&Ne.has(s.code)}function Le(s){return s===F.RECIPIENT_DISCONNECTED?"push_to_talk_agent_unavailable":s===F.CONNECTION_TIMEOUT||s===F.RESPONSE_TIMEOUT?"push_to_talk_timeout":"push_to_talk_failed"}function Ue(s){return s===F.RECIPIENT_DISCONNECTED?"The Waffle agent dropped out for a moment. Release and press again to talk.":s===F.CONNECTION_TIMEOUT||s===F.RESPONSE_TIMEOUT?"The voice turn took too long to reach the agent. Release and try again.":"That voice turn didn\u2019t go through. Press and hold to talk again."}function qe(s,e){let t=s instanceof Se?s.code:void 0,n=new d(Ue(t),void 0,Le(t),"transport",{cause:s});return n.rpcMethod=e,typeof t=="number"&&(n.livekitErrorCode=t),n}function E(s,e,t){return s instanceof d?s:new d(s instanceof Error?s.message:t,void 0,void 0,e,{cause:s})}function R(s){return s!==null&&typeof s=="object"&&!Array.isArray(s)?s:null}function Ge(s=Fe){let e;try{e=new URL(s)}catch{throw new d("apiOrigin must be a valid URL.",void 0,"invalid_api_origin")}let t=e.hostname==="localhost"||e.hostname==="127.0.0.1"||e.hostname==="[::1]";if(e.protocol!=="https:"&&!(e.protocol==="http:"&&t)||e.username||e.password||e.pathname!=="/"&&e.pathname!==""||e.search||e.hash)throw new d("apiOrigin must be an HTTPS origin or an HTTP loopback origin.",void 0,"invalid_api_origin");return e.origin}function T(s,e){if(typeof s!="string"||s.trim().length===0)throw new d(`Session response is missing ${e}.`);return s}function Ve(s){let e=T(s,"avatar.asset_hash");if(!We.test(e))throw new d("Session response contains an invalid Avatar asset hash.");return e}function ve(s){return typeof s=="number"&&Number.isFinite(s)?s:void 0}function A(s){return typeof s=="string"&&s.length>0?s:void 0}function ye(s,e){let t=T(s,e);try{let n=new URL(t),i=n.hostname==="localhost"||n.hostname==="127.0.0.1"||n.hostname==="[::1]";if(n.protocol!=="https:"&&!(n.protocol==="http:"&&i))throw new Error;if(n.username||n.password||n.hash)throw new Error;return n.toString()}catch{throw new d(`Received an invalid ${e}.`)}}function Be(s,e){let t=T(s,e);if(!/^\d{4}-\d{2}-\d{2}T/.test(t)||!Number.isFinite(Date.parse(t)))throw new d(`Received an invalid ${e}.`);return t}function we(s,e){let t=A(s[`${e}.speech_id`]),n=Number(s[`${e}.speech_step_index`]);return t&&Number.isSafeInteger(n)&&n>0?JSON.stringify([t,n]):null}function je(s,e,t,n){let i=R(s);if(!i)throw new d("Session response must be a JSON object.");if(i.protocol_version!==2)throw new d("Session response has an unsupported protocol version.");let o=R(i.connection);if(!o||o.transport!=="webrtc")throw new d("Session response is missing a WebRTC connection.");let r=T(o.url,"connection.url");try{let y=new URL(r),b=y.hostname==="localhost"||y.hostname==="127.0.0.1"||y.hostname==="[::1]";if(y.protocol!=="wss:"&&!(y.protocol==="ws:"&&b))throw new Error}catch{throw new d("Session response contains an invalid connection URL.")}let l=T(i.agent_id,"agent_id");if(l!==t)throw new d("Session response contains a mismatched Agent id.");let c=A(i.agent_studio_url);if(c)try{let y=new URL(c),b=y.hostname==="localhost"||y.hostname==="127.0.0.1"||y.hostname==="[::1]";if(y.protocol!=="https:"&&!(y.protocol==="http:"&&b))throw new Error}catch{throw new d("Session response contains an invalid Agent Studio URL.")}let a=A(i.avatar_id),p=R(i.avatar),g=null;if(a||p){if(!a||!p||T(p.id,"avatar.id")!==a)throw new d("Session response contains mismatched Avatar metadata.");let y=T(p.engine,"avatar.engine");if(y!=="vrm")throw new d("Session response contains an unsupported Avatar engine.");let b=T(p.model_url,"avatar.model_url");try{let u=new URL(b),k=u.hostname==="localhost"||u.hostname==="127.0.0.1"||u.hostname==="[::1]";if(u.protocol!=="https:"&&!(u.protocol==="http:"&&k))throw new Error}catch{throw new d("Session response contains an invalid Avatar model URL.")}g={id:a,name:T(p.name,"avatar.name"),engine:y,modelUrl:b,assetHash:Ve(p.asset_hash),runtimeProfile:T(p.runtime_profile,"avatar.runtime_profile"),...Ke(p.presentation_profile)}}let v=T(i.session_id,"session_id");return{id:v,agentId:l,...c?{agentStudioUrl:c}:{},...a?{avatarId:a}:{},...n?{developerFeedback:{sourceSessionId:v}}:{},avatar:g,inputMode:e,room:r,token:T(o.token,"connection.token")}}function Ke(s){if(s===void 0)return{};let e=R(s);if(!e)throw new d("Session response contains an invalid Avatar presentation profile.");let t=T(e.framing,"avatar.presentation_profile.framing"),n=T(e.attention,"avatar.presentation_profile.attention"),i=T(e.emotion,"avatar.presentation_profile.emotion");if(!["default","full","upper","face"].includes(t)||!["natural","focused","lively"].includes(n)||!["natural","subtle","expressive"].includes(i))throw new d("Session response contains an unsupported Avatar presentation profile.");return{presentationProfile:{framing:t,attention:n,emotion:i}}}function ze(s){let e=R(s);if(!e||typeof e.type!="string")return null;let t={contextId:A(e.contextId)??null,segmentId:A(e.segmentId),occurrence:ve(e.occurrence),audioTime:ve(e.audioTime)};if(e.type==="avatar-emotion"){let n=A(e.emotion);return n?{phase:"emotion",emotion:n,...t}:null}if(e.type==="avatar-vocal-action"){let n=A(e.action);return n?{phase:"vocal-action",action:n,...t}:null}return null}function He(s){if(!s||typeof s!="object")return null;let e=s;if(e.type!=="agent-declined")return null;let t=A(e.reason);return t?{reason:t}:null}function H(s){let{avatar:e,room:t,token:n,...i}=s;return i}var be=class{room;listeners=new Map;options;apiOrigin;currentStatus="disconnected";currentSession=null;startPromise=null;startVersion=null;stopPromise=null;requestAbort=null;readyWaiter=null;audio=null;audioMuted=!1;motionSync=null;motionAgentIdentity=null;motionPreparing=null;motionFeedback=[];motionFeedbackRevision=0;motionFeedbackAcknowledged=0;motionInterruptBarrier=null;motionFeedbackSending=null;currentRemoteAudioTrack=null;activeInputMode="push-to-talk";agentIdentity=null;progressExecutions=new Map;progressText=new Map;completedProgressExecutions=new Set;turnId=null;nextTurnId=0;turnControl=Promise.resolve();failedStartTurnKey=null;lifecycleVersion=0;recreateRoomOnConnect=!1;developerFeedbackGrant=null;developerFeedbackGrantPromise=null;developerFeedbackGrantAbort=null;constructor(e){if(e.avatarMotion===!0&&e.audioPlayback===!1)throw new d("avatarMotion requires SDK-owned audio playback.",void 0,"invalid_avatar_motion");if(this.options=e,this.apiOrigin=Ge(e.apiOrigin),!z.test(e.agentId))throw new d("agentId must be a UUID.",void 0,"invalid_agent_id");if(!e.startSession&&typeof e.sessionToken!="function")throw new d("sessionToken must be a function.",void 0,"invalid_session_token");this.room=this.createRoom()}createRoom(){let e=new Re({adaptiveStream:!0,dynacast:!0,audioCaptureDefaults:{voiceIsolation:!1}});return e.registerRpcMethod(re,async t=>{let n=this.lifecycleVersion,i;try{i=JSON.parse(t.payload)?.protocol}catch{}e===this.room&&this.currentStatus==="connecting"&&this.options.avatarMotion===!0&&i===W&&!e.remoteParticipants.has(t.callerIdentity)&&await this.waitForMotionParticipant(e,t.callerIdentity),this.options.avatarMotion===!0&&i===W&&this.motionPreparing&&await this.motionPreparing;let o=e.remoteParticipants.get(t.callerIdentity),r=n===this.lifecycleVersion&&e===this.room&&this.currentStatus==="connecting"&&o?.kind===N.AGENT&&(!this.agentIdentity||this.agentIdentity===t.callerIdentity)&&(!this.motionAgentIdentity||this.motionAgentIdentity===t.callerIdentity)&&this.options.avatarMotion===!0&&this.options.audioPlayback!==!1&&this.motionSync!==null&&i===W;return r&&(this.motionAgentIdentity=t.callerIdentity,this.stopAudioElement(),this.currentRemoteAudioTrack=null,this.emit("remoteAudioTrack",null),x(this,"audioTrack",null),x(this,"motionMode",!0)),JSON.stringify({accepted:r,protocol:W})}),e.registerRpcMethod(se,async t=>{if(e!==this.room||t.callerIdentity!==this.motionAgentIdentity)throw new Error("Untrusted motion cancellation.");let{id:n}=ue.parse(JSON.parse(t.payload)),i=this.motionSync?.cancel(n);return i?JSON.stringify(i):"ok"}),e.registerRpcMethod(de,async t=>{if(e!==this.room||t.callerIdentity!==this.motionAgentIdentity||!this.motionSync)throw new Error("Untrusted motion caption.");try{if(new TextEncoder().encode(t.payload).byteLength>Y)throw new Error("paired_caption_too_large");return this.motionSync.addCaption(he.parse(JSON.parse(t.payload))),"{}"}catch(n){throw this.failMotion(n),n}}),e.on(C.ParticipantConnected,t=>{e===this.room&&this.handleParticipant(t)}),e.on(C.ParticipantDisconnected,t=>{e!==this.room||t.kind!==N.AGENT||(this.agentIdentity=null,this.currentStatus!=="disconnecting"&&(this.handleDisconnected("The Waffle Agent disconnected."),e.disconnect().catch(n=>{this.emit("error",E(n,"transport","Could not close the Agent room."))})))}),e.on(C.ParticipantAttributesChanged,(t,n)=>{e===this.room&&this.handleParticipant(n)}),e.on(C.TrackSubscribed,(t,n,i)=>{e===this.room&&i.kind===N.AGENT&&t.kind===K.Kind.Audio&&this.setAudioTrack(t.mediaStreamTrack)}),e.on(C.TrackUnsubscribed,(t,n,i)=>{e===this.room&&i.kind===N.AGENT&&t.kind===K.Kind.Audio&&this.setAudioTrack(null)}),e.on(C.DataReceived,(t,n,i,o)=>{if(!(e!==this.room||n?.kind!==N.AGENT||i!==xe.RELIABLE)){if(o===oe){if(n.identity!==this.motionAgentIdentity)return;try{this.motionSync?.add(fe(t))}catch(r){this.failMotion(r)}return}if(!(o!=="avatar.performance"&&o!=="animated-waffle.turn"))try{let r=JSON.parse(new TextDecoder().decode(t));if(o==="animated-waffle.turn"){let c=He(r);c&&this.emit("declined",c);return}let l=ze(r);l&&x(this,"avatar",l)}catch{}}}),e.on(C.Disconnected,()=>{e===this.room&&this.handleDisconnected("The Agent session disconnected before it was ready.")}),e.registerTextStreamHandler("lk.transcription",(t,n)=>{(async()=>{let i=this.agentIdentity?e.remoteParticipants.get(this.agentIdentity):void 0;if(e!==this.room||this.currentStatus!=="connected"||i?.kind!==N.AGENT)return;let o=t.info.attributes??{},r=A(o["lk.segment_id"]),l=A(o["lk.transcribed_track_id"]),c=o["lk.transcription_final"];if(!r||!l||c!=="true"&&c!=="false")return;let a=e.localParticipant.audioTrackPublications.has(l),p=i.audioTrackPublications.has(l);if(a===p)return;let g=a?"user":"assistant";if(n.identity!==(a?e.localParticipant.identity:i.identity))return;let v=g==="assistant"?we(o,"lk"):null;if(g==="assistant"&&!v||v&&(this.progressExecutions.set(r,v),this.completedProgressExecutions.has(v)))return;let y="";for await(let b of t){if(y+=b,e!==this.room||this.currentStatus!=="connected"||v&&this.completedProgressExecutions.has(v))return;if(y.length>0){let u={segmentId:r,role:g,text:y,complete:!1};this.progressText.set(r,u),this.emit("transcriptProgress",u)}}c==="true"&&y.length>0&&(this.emit("transcriptProgress",{segmentId:r,role:g,text:y,complete:!0}),this.progressExecutions.delete(r),this.progressText.delete(r))})().catch(i=>{this.emit("error",E(i,"transcript","Transcript stream failed."))})}),e.registerTextStreamHandler("animated-waffle.transcript",(t,n)=>{t.readAll().then(i=>{if(e!==this.room||n.identity!==this.agentIdentity||!i)return;let o=t.info.attributes??{},r=A(o["animated-waffle.message_id"]),l=o["animated-waffle.role"];if(!r||l!=="user"&&l!=="assistant"||o["animated-waffle.final"]!=="true")return;let c=l==="assistant"?we(o,"animated-waffle"):null;c&&!this.motionAgentIdentity&&this.completeTranscriptExecution(c);let a=Number(t.info.timestamp);this.emit("transcript",{id:r,role:l,text:i,final:!0,...Number.isFinite(a)&&a>0?{timestamp:new Date(a).toISOString()}:{}})}).catch(i=>{this.emit("error",E(i,"transcript","Transcript stream failed."))})}),e}get status(){return this.currentStatus}get connectionState(){return this.currentStatus}get session(){return this.currentSession?H(this.currentSession):null}get remoteAudioTrack(){return this.currentRemoteAudioTrack}get muted(){return this.audioMuted}set muted(e){this.audioMuted=e,this.audio&&(this.audio.muted=e),this.motionSync?.setMuted(e)}on(e,t){let n=this.listeners.get(e)??new Set;return n.add(t),this.listeners.set(e,n),()=>n.delete(t)}connect(e={}){if(this.currentSession&&this.status==="connected"){let i=H(this.currentSession);return this.motionSync?this.motionSync.resume().then(()=>i):Promise.resolve(i)}if(this.startPromise&&this.startVersion===this.lifecycleVersion)return this.startPromise;if(this.options.avatarMotion===!0&&this.options.audioPlayback!==!1&&typeof AudioContext<"u")try{this.prepareMotionSync().catch(()=>{})}catch(i){let o=E(i,"audio","Could not prepare avatar motion audio.");return this.fail(o),Promise.reject(o)}let t=++this.lifecycleVersion,n=(async()=>{let i=this.stopPromise;if(i&&await i,t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");if(this.options.avatarMotion===!0&&typeof AudioContext<"u")try{await this.prepareMotionSync()}catch(o){if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");let r=E(o,"audio","Could not prepare avatar motion audio.");throw this.fail(r),r}if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");return this.recreateRoomOnConnect&&(this.room=this.createRoom(),this.recreateRoomOnConnect=!1),this.startInternal(e,t)})().finally(()=>{this.startPromise===n&&(this.startPromise=null,this.startVersion=null)});return this.startVersion=t,this.startPromise=n,n}disconnect(){if(this.lifecycleVersion+=1,this.stopPromise)return this.stopPromise;let e=this.stopInternal().finally(()=>{this.stopPromise===e&&(this.stopPromise=null)});return this.stopPromise=e,e}async sendText(e,t){if(!e.trim())return;this.assertConnected();let n=t?.audioResponse===!1?{"animated-waffle.audio_response":"false"}:void 0;await this.room.localParticipant.sendText(e,{topic:"lk.chat",...n?{attributes:n}:{}})}async setInputMode(e){await this.applicationControl("input_mode",{mode:e}),this.activeInputMode=e,this.turnId=null}async interrupt(){this.assertConnected();let e=this.motionAgentIdentity!==null&&this.motionSync!==null,t=this.room,n=this.motionAgentIdentity,i=this.motionSync;if(this.motionSync?.clear(),e&&(await this.waitForMotionInterruption(),!this.options.application)){if(t!==this.room||n!==this.motionAgentIdentity||i!==this.motionSync)throw new d("The interrupted session is no longer connected.");try{await t.localParticipant.performRpc({destinationIdentity:n,method:le,payload:"{}",responseTimeout:5e3})}catch(o){throw t===this.room&&i===this.motionSync&&this.failMotion(o),o}return}await this.applicationControl("interrupt",{})}get microphoneTrack(){return this.room.localParticipant.getTrackPublication(K.Source.Microphone)?.audioTrack?.mediaStreamTrack??null}async setMicrophoneEnabled(e){if(this.assertConnected(),e&&this.options.inputAudioEnabled===!1)throw new d("Input audio is disabled.",void 0,"input_audio_disabled");await this.room.localParticipant.setMicrophoneEnabled(e)}async applicationControl(e,t){if(this.assertConnected(),!this.options.application||!this.agentIdentity)throw new d("Application sessions are required.",void 0,"application_session_required");return this.room.localParticipant.performRpc({destinationIdentity:this.agentIdentity,method:"application.control",payload:JSON.stringify({action:e,...t})})}async loadDeveloperFeedback(){return this.requestDeveloperFeedback("GET")}async submitDeveloperFeedback(e){let t=e.id??crypto.randomUUID();if(!z.test(t))throw new d("Developer Feedback id must be a UUID.",void 0,"invalid_feedback");if(e.scope==="message"&&!e.targetMessageId)throw new d("Message feedback requires a persisted Assistant message id.",void 0,"invalid_feedback");if(e.scope==="session"&&e.targetMessageId)throw new d("Session feedback cannot target one message.",void 0,"invalid_feedback");if(e.targetMessageId&&!z.test(e.targetMessageId))throw new d("Developer Feedback targetMessageId must be a UUID.",void 0,"invalid_feedback");if(e.action==="change"&&!e.note?.trim()&&!e.preferredReply?.trim()&&e.dimension!=="avatar"&&e.dimension!=="voice")throw new d("Describe the requested change or provide a preferred reply.",void 0,"invalid_feedback");return this.requestDeveloperFeedback("POST",{id:t,scope:e.scope,targetMessageId:e.targetMessageId??null,action:e.action,dimension:e.dimension,note:e.note??null,preferredReply:e.preferredReply??null})}startPushToTalk(){if(this.assertPushToTalkAvailable(),this.turnId!==null)return this.turnControl;this.nextTurnId+=1,this.turnId=this.nextTurnId;try{return this.sendInputControl("start_turn",String(this.turnId))}catch(e){throw this.turnId=null,e}}commitPushToTalk(){if(this.assertPushToTalkAvailable(),this.turnId===null)return this.turnControl;let e=String(this.turnId);return this.turnId=null,this.sendInputControl("end_turn",e)}stopPushToTalk(){this.commitPushToTalk()}cancelPushToTalk(){if(this.assertPushToTalkAvailable(),this.turnId===null)return this.turnControl;let e=String(this.turnId);return this.turnId=null,this.sendInputControl("cancel_turn",e)}async startInternal(e,t){this.activeInputMode=e.inputMode??"push-to-talk",this.setStatus("connecting");let n=new AbortController;this.requestAbort=n;let i=e.sessionId??crypto.randomUUID(),o=crypto.randomUUID(),r=this.options.startSession,l=r?void 0:this.options.sessionContext,c=r?void 0:JSON.stringify({chat_session_id:i,input_mode:this.activeInputMode==="push-to-talk"?"push_to_talk":"automatic",...e.overrides?{overrides:e.overrides}:{},...l?.userName?.trim()?{user_name:l.userName}:{},...l?.timeZone?.trim()?{time_zone:l.timeZone}:{}}),a=()=>this.options.startSession?Promise.resolve(this.options.startSession({agentId:this.options.agentId,inputMode:this.activeInputMode,requestId:o,sessionId:i,reportedSource:this.options.reportedSource??"SDK",...e.overrides?{overrides:e.overrides}:{},signal:n.signal})):this.resolveSessionToken().then(h=>fetch(this.apiOrigin+"/v1/agents/"+encodeURIComponent(this.options.agentId)+"/sessions",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json",Authorization:"Bearer "+h,"Idempotency-Key":o,"X-Animated-Waffle-Reported-Source":this.options.reportedSource??"SDK"},signal:n.signal,body:c})),p;try{p=await this.withTimeout(r?a():a().catch(h=>{if(n.signal.aborted)throw h;return a()}),ge,"Timed out creating the Waffle session.",()=>n.abort())}catch(h){let w=E(h,"session","Could not create session.");throw t!==this.lifecycleVersion?new d("The connection attempt was cancelled."):(this.fail(w),w)}finally{this.requestAbort===n&&(this.requestAbort=null)}if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");let g=await p.json().catch(()=>null);if(!p.ok){let h=R(g),w=new d(A(h?.detail)??"Could not create session.",p.status,A(h?.code));throw this.fail(w),w}let v;try{v=je(g,this.activeInputMode,this.options.agentId,typeof this.options.developerFeedback=="function")}catch(h){let w=E(h,"session","Could not parse the Waffle session.");throw this.fail(w),w}this.currentSession?.id!==v.id&&this.clearDeveloperFeedbackGrant(),this.currentSession=v,this.emit("session",H(v)),x(this,"presentation",v.avatar),this.setStatus("connecting");let y,b,u=new Promise((h,w)=>{y=h,b=w}),k,M=new Promise((h,w)=>{k=w});this.readyWaiter={reject:h=>{b(h),k(h)},resolve:y,version:t};let _=this.room,m=!0,S=()=>m&&t===this.lifecycleVersion&&_===this.room&&this.currentStatus==="connecting",I=async()=>{if(await _.connect(v.room,v.token).catch(h=>{throw E(h,"transport","Could not connect to the Agent room.")}),!S())throw await _.disconnect().catch(()=>{}),new d("The connection attempt was cancelled.");if(this.options.avatarMotion||await _.startAudio().catch(h=>{throw E(h,"audio","Could not start Agent audio.")}),!S())throw new d("The connection attempt was cancelled.");if(this.options.inputAudioEnabled!==!1){let h=await _.localParticipant.setMicrophoneEnabled(!0).catch($=>{throw E($,"audio","Could not start the microphone.")}),w=h?.audioTrack?.mediaStreamTrack;if(!S())throw w?.stop(),await _.disconnect().catch(()=>{}),new d("The connection attempt was cancelled.");if(!h||h.isMuted||h.isUpstreamPaused||!w||!w.enabled||w.muted||w.readyState!=="live")throw new d("The microphone track did not publish.",void 0,void 0,"audio")}for(let h of _.remoteParticipants.values())this.handleParticipant(h)};try{await Promise.race([Promise.all([I().then(()=>this.withTimeout(u,Oe,"Timed out waiting for the Waffle Agent.",()=>{_.disconnect().catch(()=>{})},"transport")),u]),M])}catch(h){m=!1,this.readyWaiter?.version===t&&(this.readyWaiter=null),this.currentStatus!=="disconnected"&&await _.disconnect().catch(()=>{});let w=E(h,"transport","Could not connect.");throw t!==this.lifecycleVersion?new d("The connection attempt was cancelled."):(this.fail(w),w)}finally{m=!1,this.readyWaiter?.version===t&&(this.readyWaiter=null)}if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");return this.setStatus("connected"),H(v)}async stopInternal(){if(this.readyWaiter?.reject(new d("The connection attempt was cancelled.")),this.readyWaiter=null,this.requestAbort?.abort(),this.turnId=null,this.turnControl=Promise.resolve(),this.failedStartTurnKey=null,this.agentIdentity=null,this.clearTranscriptProgress(),this.stopAudio(),this.currentStatus!=="disconnected"){this.setStatus("disconnecting");try{await this.room.disconnect()}finally{this.emit("agentSpeaking",!1),this.setStatus("disconnected")}}}sendInputControl(e,t){if(this.activeInputMode!=="push-to-talk")throw new d('Push-to-talk controls require inputMode: "push-to-talk".');this.assertConnected();let n=this.agentIdentity;if(!n)throw new d("The Waffle Agent is not ready.",void 0,"push_to_talk_agent_unavailable","transport");let i=this.room,o=this.turnControl.then(()=>{if(e!=="start_turn"&&this.failedStartTurnKey===t){this.failedStartTurnKey=null;return}return this.performControlRpc(i,n,e,t)});return this.turnControl=o.then(()=>{},r=>{e==="start_turn"&&(this.failedStartTurnKey=t),this.turnId!==null&&String(this.turnId)===t&&(this.turnId=null),this.emit("error",qe(r,e))}),this.turnControl}async performControlRpc(e,t,n,i){let o=JSON.stringify({turnId:i}),r=n==="start_turn"?2:1,l;for(let c=1;c<=r;c+=1)try{return await e.localParticipant.performRpc({destinationIdentity:t,method:n,payload:o})}catch(a){if(l=a,c<r&&De(a))continue;throw a}throw l}assertPushToTalkAvailable(){if(this.options.inputAudioEnabled===!1)throw new d("Push-to-talk requires input audio to be enabled.",void 0,"input_audio_disabled")}assertConnected(){if(this.status!=="connected")throw new d("Start the Waffle session before sending input.")}async requestDeveloperFeedback(e,t){if(typeof this.options.developerFeedback!="function")throw new d("Developer Feedback is not enabled for this client.",void 0,"developer_feedback_disabled");let n=await this.resolveDeveloperFeedbackGrant(),i;try{i=await fetch(n.endpoint,{method:e,credentials:"omit",cache:"no-store",headers:{Accept:"application/json",Authorization:`Bearer ${n.token}`,...t?{"Content-Type":"application/json"}:{}},...t?{body:JSON.stringify(t)}:{}})}catch{throw new d("Could not save Developer Feedback.",void 0,"developer_feedback_network_error")}let o=await i.json().catch(()=>null);if(!i.ok){let p=R(o);throw new d(A(p?.detail)??"Could not save Developer Feedback.",i.status,A(p?.code))}let r=R(o),l=r?.round_id,c=r?.pending_count,a=r?.dashboard_url;if(l!==null&&(typeof l!="string"||!z.test(l))||typeof c!="number"||!Number.isSafeInteger(c)||c<0||a!==null&&typeof a!="string")throw new d("Developer Feedback response is invalid.");return typeof a=="string"&&ye(a,"dashboard_url"),{roundId:l,pendingCount:c,dashboardUrl:a}}resolveDeveloperFeedbackGrant(){let e=this.currentSession,t=this.options.developerFeedback;if(!e||typeof t!="function")throw new d("Start a Developer Feedback-enabled Waffle session first.",void 0,"developer_feedback_unavailable");if(this.developerFeedbackGrant?.sourceSessionId===e.id&&Date.parse(this.developerFeedbackGrant.expiresAt)>Date.now())return Promise.resolve(this.developerFeedbackGrant);if(this.developerFeedbackGrantPromise)return this.developerFeedbackGrantPromise;let n=new AbortController;this.developerFeedbackGrantAbort=n;let i=this.withTimeout(Promise.resolve(t({agentId:e.agentId,sourceSessionId:e.id,signal:n.signal})),ge,"Timed out requesting a Developer Feedback grant.",()=>n.abort()).then(async o=>{let r=await o.json().catch(()=>null);if(!o.ok){let a=R(r);throw new d(A(a?.detail)??"Could not authorize Developer Feedback.",o.status,A(a?.code))}let l=R(r),c={endpoint:ye(l?.endpoint,"developerFeedback.endpoint"),token:T(l?.token,"developerFeedback.token"),expiresAt:Be(l?.expires_at,"developer_feedback.expires_at"),sourceSessionId:T(l?.source_session_id,"developer_feedback.source_session_id")};if(c.sourceSessionId!==e.id)throw new d("Developer Feedback grant targets a different Session.");if(Date.parse(c.expiresAt)<=Date.now())throw new d("Developer Feedback grant is already expired.");if(this.currentSession?.id!==e.id)throw new d("The Waffle session changed while authorizing feedback.");return this.developerFeedbackGrant=c,c}).finally(()=>{this.developerFeedbackGrantPromise===i&&(this.developerFeedbackGrantPromise=null),this.developerFeedbackGrantAbort===n&&(this.developerFeedbackGrantAbort=null)});return this.developerFeedbackGrantPromise=i,i}handleParticipant(e){if(e.kind===N.AGENT){if(this.agentIdentity=e.identity,this.motionAgentIdentity||this.emit("agentSpeaking",e.attributes["lk.agent.state"]==="speaking"),e.attributes["animated-waffle.runtime.ready"]==="true"){if(this.options.avatarMotion===!0&&(this.motionAgentIdentity!==e.identity||e.attributes["animated-waffle.avatar.motion"]!==W)){this.readyWaiter?.reject(new d("The voice service did not negotiate avatar motion.",void 0,"avatar_motion_unsupported"));return}if(this.options.application&&e.attributes["animated-waffle.application.version"]!=="2"){this.readyWaiter?.reject(new d("The voice service does not support application sessions.",void 0,"application_session_unsupported"));return}this.readyWaiter?.resolve()}for(let t of e.audioTrackPublications.values()){let n=t.track;if(n?.kind===K.Kind.Audio){this.setAudioTrack(n.mediaStreamTrack);break}}}}handleDisconnected(e){this.readyWaiter?.reject(new d(e,void 0,void 0,"transport")),this.readyWaiter=null,this.turnId=null,this.turnControl=Promise.resolve(),this.failedStartTurnKey=null,this.agentIdentity=null,this.clearTranscriptProgress(),this.stopAudio(),this.emit("agentSpeaking",!1),this.setStatus("disconnected")}setAudioTrack(e){if(this.options.avatarMotion||e===this.currentRemoteAudioTrack||(this.stopAudio(!1),this.currentRemoteAudioTrack=e,this.emit("remoteAudioTrack",e),x(this,"audioTrack",e),!e||typeof document>"u")||this.options.audioPlayback===!1)return;let t=document.createElement("audio");t.autoplay=!0,t.muted=this.audioMuted,t.setAttribute("playsinline",""),t.srcObject=new MediaStream([e]),this.audio=t,t.play().catch(n=>{this.emit("error",E(n,"audio","Browser blocked agent audio."))})}stopAudioElement(){this.audio&&(this.audio.pause(),this.audio.srcObject=null,this.audio=null)}stopAudio(e=!0){if(this.stopAudioElement(),e){let t=this.motionSync;this.motionSync=null,t?.dispose(),this.motionPreparing=null,this.motionFeedback=[],this.motionInterruptBarrier?.reject(new Error("Paired playback stopped before interruption was acknowledged.")),this.motionInterruptBarrier=null,this.motionAgentIdentity=null,x(this,"motionMode",!1)}e&&this.currentRemoteAudioTrack&&(this.currentRemoteAudioTrack=null,this.emit("remoteAudioTrack",null),x(this,"audioTrack",null))}waitForMotionParticipant(e,t){return new Promise(n=>{let i=!1,o=()=>{i||(i=!0,clearTimeout(c),e.off(C.ParticipantConnected,r),e.off(C.Disconnected,o),l(),n())},r=a=>{a.identity===t&&o()},l=this.on("connectionState",a=>{a!=="connecting"&&o()}),c=setTimeout(o,8e3);e.on(C.ParticipantConnected,r),e.on(C.Disconnected,o),(e!==this.room||this.currentStatus!=="connecting"||e.remoteParticipants.has(t))&&o()})}prepareMotionSync(){if(this.motionPreparing)return this.motionPreparing;let e=new j({frame:t=>x(this,"motion",t),caption:t=>this.emit("transcriptProgress",t),progress:t=>{if(this.motionSync!==e)return;this.emit("agentSpeaking",t.state!=="stopped"&&t.replies.some(r=>r.state==="playing"));let n=this.motionFeedback.at(-1),i=r=>JSON.stringify([r.id,r.replies.map(l=>[l.id,l.state])]),o={progress:t,revision:++this.motionFeedbackRevision};if(n&&i(n.progress)===i(t)?this.motionFeedback[this.motionFeedback.length-1]=o:this.motionFeedback.push(o),this.motionFeedback.length>64){this.failMotion(new Error("paired_feedback_overflow"));return}this.motionFeedbackSending||this.sendMotionFeedback().catch(()=>{})},fail:t=>{this.motionSync===e&&this.failMotion(t)}});return e.setMuted(this.audioMuted),this.motionSync=e,this.motionPreparing=e.prepare()}sendMotionFeedback(){if(this.motionFeedbackSending)return this.motionFeedbackSending;let e=this.room,t=this.motionAgentIdentity,n=this.motionSync;if(!t||!n)return Promise.resolve();let o=(async()=>{try{for(;this.motionFeedback.length&&e===this.room&&t===this.motionAgentIdentity&&n===this.motionSync;){let{progress:r,revision:l}=this.motionFeedback.shift();await e.localParticipant.performRpc({destinationIdentity:t,method:ae,payload:JSON.stringify(r),responseTimeout:5e3}),this.motionFeedbackAcknowledged=Math.max(this.motionFeedbackAcknowledged,l),this.motionInterruptBarrier&&this.motionInterruptBarrier.revision<=this.motionFeedbackAcknowledged&&(this.motionInterruptBarrier.resolve(),this.motionInterruptBarrier=null)}}catch(r){if(e===this.room&&n===this.motionSync)throw this.failMotion(r),r}})().finally(()=>{this.motionFeedbackSending===o&&(this.motionFeedbackSending=null,this.motionFeedback.length&&this.sendMotionFeedback().catch(()=>{}))});return this.motionFeedbackSending=o,o}waitForMotionInterruption(){let e=this.motionFeedbackRevision;if(e<=this.motionFeedbackAcknowledged)return Promise.resolve();if(this.motionInterruptBarrier)return this.motionInterruptBarrier.revision=e,this.motionInterruptBarrier.promise;let t,n,i=new Promise((o,r)=>{t=o,n=r});return this.motionInterruptBarrier={revision:e,promise:i,resolve:t,reject:n},this.motionFeedbackSending||this.sendMotionFeedback().catch(()=>{}),i}failMotion(e){if(!this.motionSync)return;let t=this.room;this.fail(new d("Paired audio and avatar playback stopped. Reconnect to resume.",void 0,"avatar_playback_failed","audio",{cause:e})),t.disconnect().catch(()=>{})}completeTranscriptExecution(e){this.completedProgressExecutions.add(e);for(let[t,n]of this.progressText)this.progressExecutions.get(t)===e&&(this.emit("transcriptProgress",{...n,complete:!0}),this.progressExecutions.delete(t),this.progressText.delete(t))}clearTranscriptProgress(){this.progressExecutions.clear(),this.progressText.clear(),this.completedProgressExecutions.clear()}setStatus(e){e==="disconnected"&&(this.recreateRoomOnConnect=!0),e==="disconnected"&&this.currentSession&&(this.clearDeveloperFeedbackGrant(),this.currentSession=null,this.emit("session",null),x(this,"presentation",null)),this.currentStatus!==e&&(this.currentStatus=e,this.emit("connectionState",e),this.emit("status",e))}clearDeveloperFeedbackGrant(){this.developerFeedbackGrantAbort?.abort(),this.developerFeedbackGrantAbort=null,this.developerFeedbackGrantPromise=null,this.developerFeedbackGrant=null}fail(e){this.readyWaiter?.reject(e),this.readyWaiter=null,this.stopAudio(),this.emit("agentSpeaking",!1),this.setStatus("disconnected"),this.emit("error",e)}async resolveSessionToken(){if(!this.options.sessionToken)throw new d("No session token provider is configured.");let e=await this.options.sessionToken();if(!e.trim())throw new d("sessionToken must not be empty.",void 0,"invalid_session_token");return e}withTimeout(e,t,n,i,o="session"){return new Promise((r,l)=>{let c=setTimeout(()=>{i?.(),l(new d(n,void 0,void 0,o))},t);e.then(a=>{clearTimeout(c),r(a)},a=>{clearTimeout(c),l(a)})})}emit(e,t){this.listeners.get(e)?.forEach(n=>{n(t)})}};export{be as WaffleClient,d as WaffleError};
24
+ `;var Je=15e3,Q=class{constructor(e,t=()=>new AudioContext){this.callbacks=e;this.makeContext=t}callbacks;makeContext;context=null;graph=null;ready=null;timeline=null;clocks=[];ended=new Set;captions=new Map;visible=new Map;cancelled=new Set;cancelledSamples=new Map;animation=null;shown=null;muted=!1;failed=!1;disposed=!1;lastProgress="";lastProgressAt=-1/0;replyStates=new Map;observedPlayback=new Set;snapshot=null;heartbeat=null;prepare(){if(this.ready)return this.ready;let e=this.context=this.makeContext(),t=e.resume();e.onstatechange=()=>{e.state==="closed"&&!this.disposed?this.fail(new Error("paired_audio_closed")):(this.update(!0,!1),this.animate())};let n=URL.createObjectURL(new Blob([Pe],{type:"text/javascript"}));return this.ready=Promise.all([t,e.audioWorklet.addModule(n)]).then(()=>{if(this.disposed||this.context!==e)return;if(e.state!=="running")throw new Error("paired_audio_suspended");let i=new AudioWorkletNode(e,"waffle-paired-playout",{numberOfInputs:0,numberOfOutputs:1,outputChannelCount:[1],processorOptions:{maxSeconds:de,bufferSeconds:.1}}),o=e.createGain();o.gain.value=this.muted?0:1,i.port.onmessage=({data:r})=>{if(!(this.disposed||this.failed)){if(r.type==="failed")return this.fail(new Error("paired_worklet_failed"));r.type!=="clock"||r.id!==this.timeline?.id||(this.clocks.push(r),this.clocks=this.clocks.filter(l=>l.quantumEnd>=e.currentTime-2),this.update(!1,!1),this.animate())}},i.onprocessorerror=()=>this.fail(new Error("paired_worklet_failed")),i.connect(o),o.connect(e.destination),this.graph={node:i,gain:o},this.heartbeat=setInterval(()=>{if(this.disposed||this.failed)return;let r=this.timeline?.gapSince;if(r!=null&&performance.now()-r>=Je){this.fail(new Error(`paired_chunk_gap_timeout: seq ${this.timeline.seq+1}`));return}this.update(!0,!1)},1e3)}).finally(()=>URL.revokeObjectURL(n)),this.ready}async resume(){await this.prepare(),await this.context?.resume()}setMuted(e){this.muted=e,this.graph&&(this.graph.gain.gain.value=e?0:1)}add(e){if(this.disposed||this.failed||this.ended.has(e.id))return;if(!this.graph)throw new Error("paired_audio_not_ready");let t=this.timeline;if(t?.id!==e.id){t&&(this.clear(),this.remember(this.ended,t.id)),t=this.timeline={id:e.id,rate:e.sampleRate,nextFrame:0,seq:-1,sample:0,frames:new Map,replies:new Map,cancelled:new Set,pending:new Map,pendingBytes:0,gapSince:null},this.clocks=[],this.lastProgress="",this.lastProgressAt=-1/0,this.replyStates.clear(),this.observedPlayback.clear(),this.snapshot=null,this.visible.clear(),this.show(null),this.graph.node.port.postMessage({type:"reset",id:e.id,sampleRate:e.sampleRate});for(let[c,a]of this.captions)this.ended.has(a.id)&&this.captions.delete(c)}if(e.seq<=t.seq||t.pending.has(e.seq))return;if(e.sampleRate!==t.rate)throw new Error("paired_sample_rate_changed");let n=B(e.frameStart,e.sampleRate),i=B(e.frameStart+e.frameCount,e.sampleRate);if(e.audio.length!==i-n||e.frames.length!==e.frameCount*U)throw new Error("paired_chunk_length");if(i-t.sample>de*t.rate)throw new Error("paired_buffer_overflow");let o=e.frames.byteLength+e.audio.byteLength+new TextEncoder().encode(JSON.stringify({...e,frames:void 0,audio:void 0})).byteLength;if(t.pending.size>=Ie||t.pendingBytes+o>Te)throw new Error("paired_reorder_overflow");t.pending.set(e.seq,{chunk:e,bytes:o}),t.pendingBytes+=o;let r=t.seq,l=t.gapSince!==null;for(;t.pending.has(t.seq+1);){let c=t.pending.get(t.seq+1);this.append(t,c.chunk),t.pending.delete(t.seq),t.pendingBytes-=c.bytes}t.gapSince=t.pending.size?t.gapSince??performance.now():null,this.update(l!==(t.gapSince!==null)||r===-1,!1),this.animate()}append(e,t){if(t.frameStart!==e.nextFrame)throw new Error(`paired_frame_gap: seq ${t.seq}; frame ${e.nextFrame}->${t.frameStart}`);let n=B(t.frameStart,t.sampleRate);for(let o of t.replies){let r=e.replies.get(o.id);if(r&&(r.start!==o.start||r.end!==void 0&&o.end!==void 0&&r.end!==o.end))throw new Error("paired_reply_changed");e.replies.set(o.id,{...r,...o}),(o.cancelled||this.cancelled.has(o.id))&&(e.cancelled.add(o.id),this.cancelledSamples.has(o.id)||this.cancelledSamples.set(o.id,Math.min(o.end??1/0,Math.max(0,Math.floor(e.sample-o.start)))))}if(e.replies.size>64)throw new Error("paired_reply_overflow");for(let o=0;o<t.frameCount;o++)e.frames.set(t.frameStart+o,t.frames.slice(o*U,(o+1)*U));e.nextFrame+=t.frameCount,e.seq=t.seq;let i=Int16Array.from(t.audio);this.graph.node.port.postMessage({type:"push",start:n,audio:i,replies:t.replies},[i.buffer]);for(let o of t.replies)e.cancelled.has(o.id)&&this.graph.node.port.postMessage({type:"cancel",id:o.id})}addCaption(e){if(this.ended.has(e.id)||this.disposed||this.failed)return;let t=`${e.id}:${e.replyId}:${e.segmentId}`,n=this.captions.get(t);if(n&&(n.start!==e.start||n.text!==e.text||n.end!==void 0&&e.end!==void 0&&n.end!==e.end))throw new Error("paired_caption_changed");if(this.captions.size>=128&&!n){for(let i of this.captions.values())if(!(i.replyId===e.replyId||!this.visible.get(i.replyId)?.complete)){for(let[o,r]of this.captions)r.replyId===i.replyId&&this.captions.delete(o);if(this.captions.size<128)break}}if(this.captions.size>=128&&!n)throw new Error("paired_caption_overflow");this.captions.set(t,{...n,...e}),this.animate()}cancel(e){this.update();let t=this.timeline?.replies.get(e);return t?.end!==void 0&&this.timeline.sample>=t.start+t.end&&!this.timeline.cancelled.has(e)?this.snapshot:(this.cancelledSamples.has(e)||this.cancelledSamples.set(e,t?Math.min(t.end??1/0,Math.max(0,Math.floor(this.timeline.sample-t.start))):0),this.cancelledSamples.size>64&&this.cancelledSamples.delete(this.cancelledSamples.keys().next().value),this.remember(this.cancelled,e),this.timeline?.cancelled.add(e),this.graph?.node.port.postMessage({type:"cancel",id:e}),this.show(null),this.update(!0),this.snapshot)}clear(){for(let e of this.timeline?.replies.values()??[])this.cancel(e.id)}fail(e){this.failed||this.disposed||(this.clear(),this.failed=!0,this.heartbeat!==null&&clearInterval(this.heartbeat),this.heartbeat=null,this.timeline?.pending.clear(),this.timeline&&(this.timeline.pendingBytes=0),this.graph?.node.port.postMessage({type:"stop"}),this.show(null),this.update(!0),this.callbacks.fail(e))}dispose(){this.disposed||(this.clear(),this.disposed=!0,this.heartbeat!==null&&clearInterval(this.heartbeat),this.heartbeat=null,this.graph?.node.port.postMessage({type:"stop"}),this.graph&&(this.graph.node.port.onmessage=null,this.graph.node.disconnect(),this.graph.gain.disconnect()),this.graph=null,this.animation!==null&&cancelAnimationFrame(this.animation),this.animation=null,this.context&&(this.context.onstatechange=null,this.context.close().catch(()=>{})),this.context=null,this.timeline=null,this.clocks=[],this.captions.clear(),this.visible.clear(),this.show(null))}audibleClock(){let e=this.context,t=this.timeline;if(!e||!t)return null;if(e.state!=="running")return{sample:t.sample,epoch:this.clocks.at(-1)?.epoch??0,state:this.failed?"stopped":"buffering"};let n=e.getOutputTimestamp?.(),i=typeof n?.performanceTime=="number"&&n.performanceTime>0?Math.max(0,(performance.now()-n.performanceTime)/1e3):0,o=typeof n?.contextTime=="number"&&n.contextTime>0?Math.min(e.currentTime,n.contextTime+i):Math.max(0,e.currentTime-(e.outputLatency||0)-(e.baseLatency||0)),r=[...this.clocks].reverse().find(a=>a.contextStart<=o);if(!r)return{sample:t.sample,epoch:0,state:"buffering"};let l=r.contextEnd>r.contextStart?Math.min(1,Math.max(0,(o-r.contextStart)/(r.contextEnd-r.contextStart))):0,c=r.start+(r.sample-r.start)*l;return{sample:Math.max(t.sample,c),epoch:r.epoch,state:this.failed?"stopped":e.state!=="running"||o>=r.contextEnd?"buffering":"playing"}}update(e=!1,t=!0){let n=this.timeline,i=this.audibleClock();if(!n||!i)return!1;n.sample=i.sample;let o=[];for(let p of n.replies.values()){let T=n.cancelled.has(p.id),P=T?this.cancelledSamples.get(p.id)??0:Math.min(p.end??1/0,Math.max(0,Math.floor(n.sample-p.start)));if(n.sample<p.start&&!T||P===0&&!T&&p.end!==0)continue;let j=T?"interrupted":p.end!==void 0&&P>=p.end?"finished":"playing";o.push({id:p.id,playedSamples:P,state:j}),P>0&&!T&&!this.observedPlayback.has(p.id)&&(this.observedPlayback.add(p.id),this.callbacks.playback?.(p.id,performance.now()-P/n.rate*1e3,!this.muted)),this.caption(p,P,j!=="playing",j==="finished")}let r=[...n.replies.values()].find(p=>p.start<=n.sample&&(p.end===void 0||n.sample<p.start+p.end)),l=Math.min(n.nextFrame-1,Math.floor(n.sample*Z/n.rate)),c=n.frames.get(l);t&&!this.failed&&!(r&&n.cancelled.has(r.id))&&c&&this.show({id:`${n.id}:${i.epoch}`,index:l,values:c});let a={id:n.id,sample:Math.floor(n.sample),receivedSeq:n.seq,...n.pending.size?{missingSeq:n.seq+1}:{},state:this.failed?"stopped":i.state,replies:o.slice(-16)};this.snapshot=a;let u=a.replies.filter(p=>p.state==="finished"&&p.playedSamples>0&&!this.replyStates.has(p.id));if(u.length){let p=new Set(u.map(T=>T.id));this.callbacks.progress({...a,replies:a.replies.map(T=>p.has(T.id)?{...T,state:"playing"}:T)});for(let T of u)this.replyStates.set(T.id,"playing")}let g=JSON.stringify(a),k=o.some(p=>this.replyStates.get(p.id)!==p.state);if(e||g!==this.lastProgress&&(k||performance.now()-this.lastProgressAt>=100)){this.lastProgress=g,this.lastProgressAt=performance.now(),this.callbacks.progress(a);for(let p of o)this.replyStates.set(p.id,p.state)}let f=Math.floor(n.sample*Z/n.rate)-2;for(let p of n.frames.keys())p<f&&n.frames.delete(p);let y=o.filter(p=>p.state!=="playing").slice(0,-16);for(let p of y){n.replies.delete(p.id),n.cancelled.delete(p.id),this.visible.delete(p.id),this.replyStates.delete(p.id),this.observedPlayback.delete(p.id);for(let[T,P]of this.captions)P.replyId===p.id&&this.captions.delete(T)}return!this.disposed&&!this.failed}caption(e,t,n,i){let o=this.timeline,r=[...this.captions.values()].filter(u=>u.id===o.id&&u.replyId===e.id).sort((u,g)=>u.start-g.start);if(!r.length)return;let l=r.map(u=>{let g=Array.from(u.text),k=Math.max(0,t-u.start),f=i?g.length:u.end!==void 0?Math.floor(g.length*Math.min(1,k/Math.max(1,u.end-u.start))):Math.floor(k/o.rate*13);return g.slice(0,f).join("")}).join(""),c=this.visible.get(e.id),a={segmentId:e.id,role:"assistant",text:l.length<(c?.text.length??0)?c.text:l,complete:n};a.text&&(a.text!==c?.text||a.complete!==c.complete)&&(this.visible.set(e.id,a),this.callbacks.caption(a))}animate(){this.animation!==null||this.disposed||this.failed||typeof requestAnimationFrame!="function"||(this.animation=requestAnimationFrame(()=>{this.animation=null,this.update()&&this.animate()}))}show(e){let t=e?`${e.id}:${e.index}`:null;this.shown!==t&&(this.shown=t,this.callbacks.frame(e))}remember(e,t){e.add(t),e.size>64&&e.delete(e.values().next().value)}};function He(s,e,t){let n=s>t?s-t:s;return Number.isFinite(n)&&n>=0&&n<=e+100&&e-n<=250?n:null}var ee=class{constructor(e,t,n){this.send=e;this.inputMode=t;this.inputEnabled=n}send;inputMode;inputEnabled;clockId=crypto.randomUUID();turns=[];active;candidateStart;lastActive=0;remoteActive=!1;closed=!1;activity(e,t){if(!(this.closed||!this.inputEnabled()||!Number.isFinite(t)||t<0)){if(e){if(this.lastActive=t,!this.active){if(this.candidateStart??=t,t-this.candidateStart<80)return;let n=this.turns.at(-1);if(n&&!n.terminal&&!n.response&&n.ended!==null&&t-n.ended<1e3)this.active=n,n.ended=null;else if(this.turns.length<512){for(let i of this.turns)i.response&&!i.terminal&&this.unavailable(i,"interrupted");this.active={id:crypto.randomUUID(),started:this.candidateStart,ended:null,terminal:!1},this.turns.push(this.active),this.speech(this.active),this.remoteActive&&this.unavailable(this.active,"ambiguous_response")}}}else this.candidateStart=void 0,this.active&&t-this.lastActive>=250&&this.end();this.expire(t)}}end(){this.active&&(this.active.ended=this.lastActive,this.active.terminal||this.speech(this.active),this.active=void 0,this.candidateStart=void 0)}bind(e){if(this.closed)return null;if(!e.inputId){for(let o of this.turns)o.terminal||this.unavailable(o,"ambiguous_response");return null}let t=this.turns.find(o=>o.response===e.responseId);if(t)return{clockId:this.clockId,turnId:t.id};let n=this.turns.filter(o=>!o.terminal&&!o.response);if(n.length!==1||n[0].ended===null){for(let o of n)this.unavailable(o,"ambiguous_response");return null}let i=n[0];return i.response=e.responseId,this.flush(i),{clockId:this.clockId,turnId:i.id}}playback(e,t,n){if(this.closed||!Number.isFinite(t)||t<0)return;let i=e&&!this.remoteActive;if(this.remoteActive=e,!i)return;let o=this.turns.filter(l=>!l.terminal&&l.playback===void 0);if(!n||o.length!==1||o[0].ended===null||!o[0].response){for(let l of o)this.unavailable(l,n?"ambiguous_response":"playback_unavailable");return}let r=o[0];t<r.ended||(r.playback=t,r.source="rtc_playout_estimate",this.flush(r))}pairedPlayback(e,t,n){if(this.closed||!Number.isFinite(t)||t<0)return;let i=this.turns.find(o=>o.response===e&&!o.terminal);if(i){if(!n)return this.unavailable(i,"playback_unavailable");if(i.ended===null||t<i.ended)return this.unavailable(i,"speech_end_unavailable");i.playback=t,i.source="paired_audio_output_estimate",this.flush(i)}}interrupted(e){let t=this.turns.find(n=>n.response===e&&!n.terminal);t&&this.unavailable(t,"interrupted")}cancel(){for(let e of this.turns)e.terminal||this.unavailable(e,"cancelled");this.active=void 0,this.candidateStart=void 0}expire(e){for(let t of this.turns)!t.terminal&&e-(t.ended??t.started)>6e4&&this.unavailable(t,"timeout")}close(){this.closed=!0;for(let e of this.turns)e.terminal||this.unavailable(e,"disconnected")}speech(e){this.send({kind:"speech",clockId:this.clockId,turnId:e.id,startedMs:e.started,endedMs:e.ended,inputMode:this.inputMode()})}flush(e){!e.response||e.playback===void 0||e.terminal||(e.terminal=!0,this.send({kind:"playback",clockId:this.clockId,turnId:e.id,responseId:e.response,playbackMs:e.playback,source:e.source}))}unavailable(e,t){e.terminal||(e.terminal=!0,this.send({kind:"unavailable",clockId:this.clockId,turnId:e.id,reason:t}))}},$e=`
25
+ class WaffleAudioActivity extends AudioWorkletProcessor {
26
+ process(inputs) {
27
+ const channel = inputs[0]?.[0];
28
+ if (channel?.length) {
29
+ let energy = 0;
30
+ for (const sample of channel) energy += sample * sample;
31
+ if (currentFrame % 1024 === 0) this.port.postMessage({
32
+ active: energy / channel.length > 0.0001, at: currentTime
33
+ });
34
+ }
35
+ return true;
36
+ }
37
+ }
38
+ registerProcessor('waffle-audio-activity', WaffleAudioActivity);
39
+ `,te=class{constructor(e,t){this.tracker=e;this.playable=t}tracker;playable;origin=performance.now();context;node;source;receiver;outputSource;outputNode;outputActivity;timer;closed=!1;lastPacketAt=-1;ready;prepare(){this.ready||typeof AudioContext>"u"||(this.ready=(async()=>{let e=this.context=new AudioContext;await e.resume();let t=URL.createObjectURL(new Blob([$e],{type:"text/javascript"}));try{await e.audioWorklet.addModule(t)}finally{URL.revokeObjectURL(t)}if(this.closed)return;let n=this.node=new AudioWorkletNode(e,"waffle-audio-activity"),i=e.createGain();i.gain.value=0,n.connect(i).connect(e.destination),n.port.onmessage=({data:o})=>{if(this.closed||e.state!=="running")return;let r=performance.now()-this.origin-Math.max(0,e.currentTime-o.at)*1e3;this.tracker.activity(o.active,Math.max(0,r))},this.timer=setInterval(()=>{let o=performance.now()-this.origin;this.tracker.expire(o);let r;try{r=this.receiver?.getSynchronizationSources?.()[0]}catch{return}if(!r||r.timestamp===this.lastPacketAt)return;let l=r.audioLevel!==void 0?r.audioLevel>.01:this.outputActivity&&o-this.outputActivity.at<=250?this.outputActivity.active:void 0;if(l===void 0)return;this.lastPacketAt=r.timestamp;let c=He(r.timestamp,o+this.origin,performance.timeOrigin);if(c===null)return;let a=c-this.origin;a<0||this.tracker.playback(l,a,this.playable())},20)})().catch(()=>{clearInterval(this.timer),this.node?.disconnect(),this.node=void 0,this.context?.close().catch(()=>{}),this.context=void 0}))}async microphone(e){if(await this.ready,!(this.closed||!this.context||!this.node))try{this.source?.disconnect(),this.source=this.context.createMediaStreamSource(new MediaStream([e])),this.source.connect(this.node)}catch{}}output(e){this.receiver!==e&&(this.receiver=e,this.lastPacketAt=-1,this.outputSource?.disconnect(),this.outputNode?.disconnect(),this.outputSource=void 0,this.outputNode=void 0,this.outputActivity=void 0,e&&this.ready?.then(()=>{let t=this.context;if(this.closed||this.receiver!==e||!t||!this.node)return;let n=this.outputSource=t.createMediaStreamSource(new MediaStream([e.track])),i=this.outputNode=new AudioWorkletNode(t,"waffle-audio-activity");n.connect(i).connect(t.destination),i.port.onmessage=({data:o})=>{this.closed||this.receiver!==e||t.state!=="running"||(this.outputActivity={active:o.active,at:performance.now()-this.origin-Math.max(0,t.currentTime-o.at)*1e3})}}).catch(()=>{this.receiver===e&&(this.outputSource?.disconnect(),this.outputNode?.disconnect(),this.outputActivity=void 0)}))}pairedPlayback(e,t,n){this.tracker.pairedPlayback(e,t-this.origin,n)}close(){this.closed=!0,this.tracker.close(),clearInterval(this.timer),this.source?.disconnect(),this.node?.disconnect(),this.outputSource?.disconnect(),this.outputNode?.disconnect(),this.context?.close().catch(()=>{})}};import{z as w}from"zod";var ce="waffle.audio-timing.v1",R=w.string().uuid(),z=w.number().finite().nonnegative().max(864e5),Rt=w.discriminatedUnion("kind",[w.object({kind:w.literal("speech"),clockId:R,turnId:R,startedMs:z,endedMs:z.nullable(),inputMode:w.enum(["automatic","push-to-talk"])}).strict().refine(s=>s.endedMs===null||s.endedMs>=s.startedMs),w.object({kind:w.literal("playback"),clockId:R,turnId:R,responseId:w.string().min(1).max(128),playbackMs:z,source:w.enum(["rtc_playout_estimate","paired_audio_output_estimate"])}).strict(),w.object({kind:w.literal("unavailable"),clockId:R,turnId:R,reason:w.enum(["cancelled","interrupted","disconnected","timeout","ambiguous_response","playback_unavailable","speech_end_unavailable"])}).strict()]),xe=w.object({responseId:w.string().min(1).max(128),inputId:R.nullable()}).strict(),Ee=w.enum(["native_playback_unavailable","custom_playback","avatar_playback_unavailable","audio_observer_unavailable"]),Ct=w.union([w.object({clockId:R,turnId:R}).strict(),w.object({clockId:R,reason:Ee}).strict()]).nullable(),Ot=w.object({clockId:R,clientTurnId:R.optional(),contextId:R.optional(),speechId:w.string().min(1).max(128).optional(),clientStartedMs:z.optional(),clientEndedMs:z.optional(),clientPlaybackMs:z.optional(),inputMode:w.enum(["automatic","push-to-talk"]).optional(),source:w.enum(["client_audio_activity_estimate","rtc_playout_estimate","paired_audio_output_estimate"]).optional(),reason:w.enum(["cancelled","interrupted","disconnected","timeout","ambiguous_response","playback_unavailable","speech_end_unavailable",...Ee.options]).optional()}).strict();import{DataPacket_Kind as Xe,ParticipantKind as L,Room as Ye,RoomEvent as O,RpcError as Fe,Track as ne}from"livekit-client";var Re=15e3,Ze=3e4,Qe="https://animated-waffle.narya.ai",ie=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,et=/^sha256:[a-f0-9]{64}$/,d=class extends Error{constructor(t,n,i,o="session",r){super(t,r);this.status=n;this.code=i;this.stage=o;this.name="WaffleError"}status;code;stage;rpcMethod;livekitErrorCode},q={APPLICATION_ERROR:1500,CONNECTION_TIMEOUT:1501,RESPONSE_TIMEOUT:1502,RECIPIENT_DISCONNECTED:1503},tt=new Set([q.CONNECTION_TIMEOUT,q.RESPONSE_TIMEOUT]);function nt(s){return s instanceof Fe&&tt.has(s.code)}function it(s){return s===q.RECIPIENT_DISCONNECTED?"push_to_talk_agent_unavailable":s===q.CONNECTION_TIMEOUT||s===q.RESPONSE_TIMEOUT?"push_to_talk_timeout":"push_to_talk_failed"}function ot(s){return s===q.RECIPIENT_DISCONNECTED?"The Waffle agent dropped out for a moment. Release and press again to talk.":s===q.CONNECTION_TIMEOUT||s===q.RESPONSE_TIMEOUT?"The voice turn took too long to reach the agent. Release and try again.":"That voice turn didn\u2019t go through. Press and hold to talk again."}function rt(s,e){let t=s instanceof Fe?s.code:void 0,n=new d(ot(t),void 0,it(t),"transport",{cause:s});return n.rpcMethod=e,typeof t=="number"&&(n.livekitErrorCode=t),n}function C(s,e,t){return s instanceof d?s:new d(s instanceof Error?s.message:t,void 0,void 0,e,{cause:s})}function F(s){return s!==null&&typeof s=="object"&&!Array.isArray(s)?s:null}function st(s=Qe){let e;try{e=new URL(s)}catch{throw new d("apiOrigin must be a valid URL.",void 0,"invalid_api_origin")}let t=e.hostname==="localhost"||e.hostname==="127.0.0.1"||e.hostname==="[::1]";if(e.protocol!=="https:"&&!(e.protocol==="http:"&&t)||e.username||e.password||e.pathname!=="/"&&e.pathname!==""||e.search||e.hash)throw new d("apiOrigin must be an HTTPS origin or an HTTP loopback origin.",void 0,"invalid_api_origin");return e.origin}function _(s,e){if(typeof s!="string"||s.trim().length===0)throw new d(`Session response is missing ${e}.`);return s}function at(s){let e=_(s,"avatar.asset_hash");if(!et.test(e))throw new d("Session response contains an invalid Avatar asset hash.");return e}function Ce(s){return typeof s=="number"&&Number.isFinite(s)?s:void 0}function M(s){return typeof s=="string"&&s.length>0?s:void 0}function Oe(s,e){let t=_(s,e);try{let n=new URL(t),i=n.hostname==="localhost"||n.hostname==="127.0.0.1"||n.hostname==="[::1]";if(n.protocol!=="https:"&&!(n.protocol==="http:"&&i))throw new Error;if(n.username||n.password||n.hash)throw new Error;return n.toString()}catch{throw new d(`Received an invalid ${e}.`)}}function lt(s,e){let t=_(s,e);if(!/^\d{4}-\d{2}-\d{2}T/.test(t)||!Number.isFinite(Date.parse(t)))throw new d(`Received an invalid ${e}.`);return t}function We(s,e){let t=M(s[`${e}.speech_id`]),n=Number(s[`${e}.speech_step_index`]);return t&&Number.isSafeInteger(n)&&n>0?JSON.stringify([t,n]):null}function dt(s,e,t,n){let i=F(s);if(!i)throw new d("Session response must be a JSON object.");if(i.protocol_version!==2)throw new d("Session response has an unsupported protocol version.");let o=F(i.connection);if(!o||o.transport!=="webrtc")throw new d("Session response is missing a WebRTC connection.");let r=_(o.url,"connection.url");try{let f=new URL(r),y=f.hostname==="localhost"||f.hostname==="127.0.0.1"||f.hostname==="[::1]";if(f.protocol!=="wss:"&&!(f.protocol==="ws:"&&y))throw new Error}catch{throw new d("Session response contains an invalid connection URL.")}let l=_(i.agent_id,"agent_id");if(l!==t)throw new d("Session response contains a mismatched Agent id.");let c=M(i.agent_studio_url);if(c)try{let f=new URL(c),y=f.hostname==="localhost"||f.hostname==="127.0.0.1"||f.hostname==="[::1]";if(f.protocol!=="https:"&&!(f.protocol==="http:"&&y))throw new Error}catch{throw new d("Session response contains an invalid Agent Studio URL.")}let a=M(i.avatar_id),u=F(i.avatar),g=null;if(a||u){if(!a||!u||_(u.id,"avatar.id")!==a)throw new d("Session response contains mismatched Avatar metadata.");let f=_(u.engine,"avatar.engine");if(f!=="vrm")throw new d("Session response contains an unsupported Avatar engine.");let y=_(u.model_url,"avatar.model_url");try{let p=new URL(y),T=p.hostname==="localhost"||p.hostname==="127.0.0.1"||p.hostname==="[::1]";if(p.protocol!=="https:"&&!(p.protocol==="http:"&&T))throw new Error}catch{throw new d("Session response contains an invalid Avatar model URL.")}g={id:a,name:_(u.name,"avatar.name"),engine:f,modelUrl:y,assetHash:at(u.asset_hash),runtimeProfile:_(u.runtime_profile,"avatar.runtime_profile"),...ct(u.presentation_profile)}}let k=_(i.session_id,"session_id");return{id:k,agentId:l,...c?{agentStudioUrl:c}:{},...a?{avatarId:a}:{},...n?{developerFeedback:{sourceSessionId:k}}:{},avatar:g,inputMode:e,room:r,token:_(o.token,"connection.token")}}function ct(s){if(s===void 0)return{};let e=F(s);if(!e)throw new d("Session response contains an invalid Avatar presentation profile.");let t=_(e.framing,"avatar.presentation_profile.framing"),n=_(e.attention,"avatar.presentation_profile.attention"),i=_(e.emotion,"avatar.presentation_profile.emotion");if(!["default","full","upper","face"].includes(t)||!["natural","focused","lively"].includes(n)||!["natural","subtle","expressive"].includes(i))throw new d("Session response contains an unsupported Avatar presentation profile.");return{presentationProfile:{framing:t,attention:n,emotion:i}}}function ut(s){let e=F(s);if(!e||typeof e.type!="string")return null;let t={contextId:M(e.contextId)??null,segmentId:M(e.segmentId),occurrence:Ce(e.occurrence),audioTime:Ce(e.audioTime)};if(e.type==="avatar-emotion"){let n=M(e.emotion);return n?{phase:"emotion",emotion:n,...t}:null}if(e.type==="avatar-vocal-action"){let n=M(e.action);return n?{phase:"vocal-action",action:n,...t}:null}return null}function pt(s){if(!s||typeof s!="object")return null;let e=s;if(e.type!=="agent-declined")return null;let t=M(e.reason);return t?{reason:t}:null}function oe(s){let{avatar:e,room:t,token:n,...i}=s;return i}var Ne=class{connectionStartedAt=0;diagnosticState=new X(e=>this.emit("diagnostics",e));get diagnostics(){return this.diagnosticState.snapshot}room;listeners=new Map;options;apiOrigin;currentStatus="disconnected";currentSession=null;startPromise=null;startVersion=null;stopPromise=null;requestAbort=null;readyWaiter=null;audio=null;audioTiming=null;audioTimingPending=Promise.resolve();audioTimingClockId=crypto.randomUUID();audioMuted=!1;motionSync=null;motionAgentIdentity=null;motionPreparing=null;motionFeedback=[];motionFeedbackRevision=0;motionFeedbackAcknowledged=0;motionInterruptBarrier=null;motionFeedbackSending=null;currentRemoteAudioTrack=null;activeInputMode="push-to-talk";agentIdentity=null;progressExecutions=new Map;progressText=new Map;completedProgressExecutions=new Set;turnId=null;nextTurnId=0;turnControl=Promise.resolve();failedStartTurnKey=null;lifecycleVersion=0;recreateRoomOnConnect=!1;developerFeedbackGrant=null;developerFeedbackGrantPromise=null;developerFeedbackGrantAbort=null;constructor(e){if(e.avatarMotion===!0&&e.audioPlayback===!1)throw new d("avatarMotion requires SDK-owned audio playback.",void 0,"invalid_avatar_motion");if(this.options=e,this.apiOrigin=st(e.apiOrigin),!ie.test(e.agentId))throw new d("agentId must be a UUID.",void 0,"invalid_agent_id");if(!e.startSession&&typeof e.sessionToken!="function")throw new d("sessionToken must be a function.",void 0,"invalid_session_token");this.room=this.createRoom(),ae(this,"avatarLoad",t=>this.diagnosticState.update({avatar:t})),ae(this,"avatarError",t=>this.diagnosticState.update({avatar:"error",error:t}))}createRoom(){let e=new Ye({adaptiveStream:!0,dynacast:!0,audioCaptureDefaults:{voiceIsolation:!1}});return e.registerRpcMethod(ce,async t=>{if(e!==this.room||t.callerIdentity!==this.agentIdentity||e.remoteParticipants.get(t.callerIdentity)?.kind!==L.AGENT)throw new Error("Untrusted audio timing binding.");if(t.payload.length>512)throw new Error("Audio timing binding is too large.");let n=xe.parse(JSON.parse(t.payload));return await this.audioTimingPending,e!==this.room||t.callerIdentity!==this.agentIdentity?"null":JSON.stringify(this.audioTiming?this.audioTiming.tracker.bind(n):{clockId:this.audioTimingClockId,reason:this.audioTimingUnavailableReason()})}),e.registerRpcMethod(ye,async t=>{let n=this.lifecycleVersion,i;try{i=JSON.parse(t.payload)?.protocol}catch{}e===this.room&&this.currentStatus==="connecting"&&this.options.avatarMotion===!0&&i===V&&!e.remoteParticipants.has(t.callerIdentity)&&await this.waitForMotionParticipant(e,t.callerIdentity),this.options.avatarMotion===!0&&i===V&&this.motionPreparing&&await this.motionPreparing;let o=e.remoteParticipants.get(t.callerIdentity),r=n===this.lifecycleVersion&&e===this.room&&this.currentStatus==="connecting"&&o?.kind===L.AGENT&&(!this.agentIdentity||this.agentIdentity===t.callerIdentity)&&(!this.motionAgentIdentity||this.motionAgentIdentity===t.callerIdentity)&&this.options.avatarMotion===!0&&this.options.audioPlayback!==!1&&this.motionSync!==null&&i===V;return r&&(this.motionAgentIdentity=t.callerIdentity,this.stopAudioElement(),this.currentRemoteAudioTrack=null,this.emit("remoteAudioTrack",null),N(this,"audioTrack",null),N(this,"motionMode",!0)),JSON.stringify({accepted:r,protocol:V})}),e.registerRpcMethod(be,async t=>{if(e!==this.room||t.callerIdentity!==this.motionAgentIdentity)throw new Error("Untrusted motion cancellation.");let{id:n}=Ae.parse(JSON.parse(t.payload)),i=this.motionSync?.cancel(n);return i?JSON.stringify(i):"ok"}),e.registerRpcMethod(we,async t=>{if(e!==this.room||t.callerIdentity!==this.motionAgentIdentity||!this.motionSync)throw new Error("Untrusted motion caption.");try{if(new TextEncoder().encode(t.payload).byteLength>le)throw new Error("paired_caption_too_large");return this.motionSync.addCaption(_e.parse(JSON.parse(t.payload))),"{}"}catch(n){throw this.failMotion(n),n}}),e.on(O.ParticipantConnected,t=>{e===this.room&&this.handleParticipant(t)}),e.on(O.ParticipantDisconnected,t=>{e!==this.room||t.kind!==L.AGENT||(this.agentIdentity=null,this.currentStatus!=="disconnecting"&&(this.handleDisconnected("The Waffle Agent disconnected."),e.disconnect().catch(n=>{this.emit("error",C(n,"transport","Could not close the Agent room."))})))}),e.on(O.ParticipantAttributesChanged,(t,n)=>{e===this.room&&this.handleParticipant(n)}),e.on(O.TrackSubscribed,(t,n,i)=>{e===this.room&&i.kind===L.AGENT&&t.kind===ne.Kind.Audio&&(this.setAudioTrack(t.mediaStreamTrack),this.audioTiming?.output(t.receiver))}),e.on(O.TrackUnsubscribed,(t,n,i)=>{e===this.room&&i.kind===L.AGENT&&t.kind===ne.Kind.Audio&&(this.setAudioTrack(null),this.audioTiming?.output(void 0))}),e.on(O.DataReceived,(t,n,i,o)=>{if(!(e!==this.room||n?.kind!==L.AGENT||i!==Xe.RELIABLE)){if(o===he){if(n.identity!==this.agentIdentity||t.byteLength>4096)return;try{let r=fe.parse(JSON.parse(new TextDecoder().decode(t)));r.sessionId===this.currentSession?.id&&this.diagnosticState.update({server:r})}catch{}return}if(o===ve){if(n.identity!==this.motionAgentIdentity)return;try{this.motionSync?.add(Me(t))}catch(r){this.failMotion(r)}return}if(!(o!=="avatar.performance"&&o!=="animated-waffle.turn"))try{let r=JSON.parse(new TextDecoder().decode(t));if(o==="animated-waffle.turn"){let c=pt(r);c&&this.emit("declined",c);return}let l=ut(r);l&&N(this,"avatar",l)}catch{}}}),e.on(O.Disconnected,()=>{e===this.room&&this.handleDisconnected("The Agent session disconnected before it was ready.")}),e.registerTextStreamHandler("lk.transcription",(t,n)=>{(async()=>{let i=this.agentIdentity?e.remoteParticipants.get(this.agentIdentity):void 0;if(e!==this.room||this.currentStatus!=="connected"||i?.kind!==L.AGENT)return;let o=t.info.attributes??{},r=M(o["lk.segment_id"]),l=M(o["lk.transcribed_track_id"]),c=o["lk.transcription_final"];if(!r||!l||c!=="true"&&c!=="false")return;let a=e.localParticipant.audioTrackPublications.has(l),u=i.audioTrackPublications.has(l);if(a===u)return;let g=a?"user":"assistant";if(n.identity!==(a?e.localParticipant.identity:i.identity))return;let k=g==="assistant"?We(o,"lk"):null;if(g==="assistant"&&!k||k&&(this.progressExecutions.set(r,k),this.completedProgressExecutions.has(k)))return;let f="";for await(let y of t){if(f+=y,e!==this.room||this.currentStatus!=="connected"||k&&this.completedProgressExecutions.has(k))return;if(f.length>0){let p={segmentId:r,role:g,text:f,complete:!1};this.progressText.set(r,p),this.emit("transcriptProgress",p)}}c==="true"&&f.length>0&&(this.emit("transcriptProgress",{segmentId:r,role:g,text:f,complete:!0}),this.progressExecutions.delete(r),this.progressText.delete(r))})().catch(i=>{this.emit("error",C(i,"transcript","Transcript stream failed."))})}),e.registerTextStreamHandler("animated-waffle.transcript",(t,n)=>{t.readAll().then(i=>{if(e!==this.room||n.identity!==this.agentIdentity||!i)return;let o=t.info.attributes??{},r=M(o["animated-waffle.message_id"]),l=o["animated-waffle.role"];if(!r||l!=="user"&&l!=="assistant"||o["animated-waffle.final"]!=="true")return;let c=l==="assistant"?We(o,"animated-waffle"):null;c&&!this.motionAgentIdentity&&this.completeTranscriptExecution(c);let a=Number(t.info.timestamp);this.emit("transcript",{id:r,role:l,text:i,final:!0,...Number.isFinite(a)&&a>0?{timestamp:new Date(a).toISOString()}:{}})}).catch(i=>{this.emit("error",C(i,"transcript","Transcript stream failed."))})}),e}get status(){return this.currentStatus}audioTimingUnavailableReason(){return this.options.audioPlayback===!1?"custom_playback":"audio_observer_unavailable"}get connectionState(){return this.currentStatus}get session(){return this.currentSession?oe(this.currentSession):null}get remoteAudioTrack(){return this.currentRemoteAudioTrack}get muted(){return this.audioMuted}set muted(e){this.audioMuted=e,this.audio&&(this.audio.muted=e),this.motionSync?.setMuted(e),this.diagnosticState.update({playback:this.options.audioPlayback===!1?"custom":e?"muted":"idle"})}on(e,t){let n=this.listeners.get(e)??new Set;return n.add(t),this.listeners.set(e,n),()=>n.delete(t)}prepareAudioTiming(){if(!this.audioTiming&&this.options.inputAudioEnabled!==!1&&this.options.audioPlayback!==!1&&typeof AudioContext<"u"){let e=new ee(t=>{this.diagnosticState.audio(t);let n=this.room,i=this.agentIdentity;this.audioTimingPending=this.audioTimingPending.then(async()=>{!i||n!==this.room||await n.localParticipant.performRpc({destinationIdentity:i,method:ce,payload:JSON.stringify(t),responseTimeout:3})}).catch(()=>{})},()=>this.activeInputMode,()=>this.currentStatus==="connected"&&!!this.microphoneTrack?.enabled&&(this.activeInputMode==="automatic"||this.turnId!==null));this.audioTiming=new te(e,()=>!!(this.audio&&!this.audio.paused&&!this.audio.muted&&this.audio.volume>0)),this.audioTiming.prepare()}}connect(e={}){if(this.currentSession&&this.status==="connected"){let i=oe(this.currentSession);return this.motionSync?this.motionSync.resume().then(()=>i):Promise.resolve(i)}if(this.startPromise&&this.startVersion===this.lifecycleVersion)return this.startPromise;if(this.prepareAudioTiming(),this.options.avatarMotion===!0&&this.options.audioPlayback!==!1&&typeof AudioContext<"u")try{this.prepareMotionSync().catch(()=>{})}catch(i){let o=C(i,"audio","Could not prepare avatar motion audio.");return this.fail(o),Promise.reject(o)}let t=++this.lifecycleVersion,n=(async()=>{let i=this.stopPromise;if(i&&await i,t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");if(this.prepareAudioTiming(),this.options.avatarMotion===!0&&typeof AudioContext<"u")try{await this.prepareMotionSync()}catch(o){if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");let r=C(o,"audio","Could not prepare avatar motion audio.");throw this.fail(r),r}if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");return this.recreateRoomOnConnect&&(this.room=this.createRoom(),this.recreateRoomOnConnect=!1),this.startInternal(e,t)})().finally(()=>{this.startPromise===n&&(this.startPromise=null,this.startVersion=null)});return this.startVersion=t,this.startPromise=n,n}disconnect(){if(this.lifecycleVersion+=1,this.stopPromise)return this.stopPromise;let e=this.stopInternal().finally(()=>{this.stopPromise===e&&(this.stopPromise=null)});return this.stopPromise=e,e}async sendText(e,t){if(!e.trim())return;this.assertConnected();let n=t?.audioResponse===!1?{"animated-waffle.audio_response":"false"}:void 0;await this.room.localParticipant.sendText(e,{topic:"lk.chat",...n?{attributes:n}:{}})}async setInputMode(e){let t=this.lifecycleVersion;if(await this.applicationControl("input_mode",{mode:e}),t!==this.lifecycleVersion)throw new d("The session changed while switching input mode.");this.activeInputMode=e,this.diagnosticState.update({inputMode:e}),this.turnId=null}async interrupt(){this.assertConnected();let e=this.motionAgentIdentity!==null&&this.motionSync!==null,t=this.room,n=this.motionAgentIdentity,i=this.motionSync;if(this.motionSync?.clear(),e&&(await this.waitForMotionInterruption(),!this.options.application)){if(t!==this.room||n!==this.motionAgentIdentity||i!==this.motionSync)throw new d("The interrupted session is no longer connected.");try{await t.localParticipant.performRpc({destinationIdentity:n,method:Se,payload:"{}",responseTimeout:5e3})}catch(o){throw t===this.room&&i===this.motionSync&&this.failMotion(o),o}return}await this.applicationControl("interrupt",{})}get microphoneTrack(){return this.room.localParticipant.getTrackPublication(ne.Source.Microphone)?.audioTrack?.mediaStreamTrack??null}async setMicrophoneEnabled(e){if(this.assertConnected(),e&&this.options.inputAudioEnabled===!1)throw new d("Input audio is disabled.",void 0,"input_audio_disabled");let t=this.room,n=this.lifecycleVersion;this.diagnosticState.update({microphone:e?"requesting":"off"});let i=await t.localParticipant.setMicrophoneEnabled(e).catch(r=>{if(t!==this.room||n!==this.lifecycleVersion)throw r;let l=C(r,"audio","Could not change microphone state.");throw this.diagnosticState.update({microphone:"error"}),this.emit("error",l),l});if(t!==this.room||n!==this.lifecycleVersion||this.currentStatus!=="connected")return;this.diagnosticState.update({microphone:e?"on":"off"});let o=i?.audioTrack?.mediaStreamTrack;e&&o&&this.audioTiming?.microphone(o),e||this.audioTiming?.tracker.end()}async applicationControl(e,t){if(this.assertConnected(),!this.options.application||!this.agentIdentity)throw new d("Application sessions are required.",void 0,"application_session_required");return this.room.localParticipant.performRpc({destinationIdentity:this.agentIdentity,method:"application.control",payload:JSON.stringify({action:e,...t})})}async loadDeveloperFeedback(){return this.requestDeveloperFeedback("GET")}async submitDeveloperFeedback(e){let t=e.id??crypto.randomUUID();if(!ie.test(t))throw new d("Developer Feedback id must be a UUID.",void 0,"invalid_feedback");if(e.scope==="message"&&!e.targetMessageId)throw new d("Message feedback requires a persisted Assistant message id.",void 0,"invalid_feedback");if(e.scope==="session"&&e.targetMessageId)throw new d("Session feedback cannot target one message.",void 0,"invalid_feedback");if(e.targetMessageId&&!ie.test(e.targetMessageId))throw new d("Developer Feedback targetMessageId must be a UUID.",void 0,"invalid_feedback");if(e.action==="change"&&!e.note?.trim()&&!e.preferredReply?.trim()&&e.dimension!=="avatar"&&e.dimension!=="voice")throw new d("Describe the requested change or provide a preferred reply.",void 0,"invalid_feedback");return this.requestDeveloperFeedback("POST",{id:t,scope:e.scope,targetMessageId:e.targetMessageId??null,action:e.action,dimension:e.dimension,note:e.note??null,preferredReply:e.preferredReply??null})}startPushToTalk(){if(this.assertPushToTalkAvailable(),this.turnId!==null)return this.turnControl;this.nextTurnId+=1,this.turnId=this.nextTurnId;try{return this.sendInputControl("start_turn",String(this.turnId))}catch(e){throw this.turnId=null,e}}commitPushToTalk(){if(this.assertPushToTalkAvailable(),this.turnId===null)return this.turnControl;let e=String(this.turnId);return this.audioTiming?.tracker.end(),this.turnId=null,this.sendInputControl("end_turn",e)}stopPushToTalk(){this.commitPushToTalk()}cancelPushToTalk(){if(this.assertPushToTalkAvailable(),this.turnId===null)return this.turnControl;let e=String(this.turnId);return this.audioTiming?.tracker.cancel(),this.turnId=null,this.sendInputControl("cancel_turn",e)}async startInternal(e,t){if(this.activeInputMode=e.inputMode??"push-to-talk",this.diagnosticState.start(this.activeInputMode,this.options.inputAudioEnabled!==!1,this.options.audioPlayback!==!1,this.options.avatarMotion===!0),this.audioMuted&&this.diagnosticState.update({playback:"muted"}),t!==this.lifecycleVersion)throw this.diagnosticState.update({connectionState:this.currentStatus}),new d("The connection attempt was cancelled.");let n=this.connectionStartedAt=performance.now();if(this.setStatus("connecting"),t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");let i=new AbortController;this.requestAbort=i;let o=e.sessionId??crypto.randomUUID(),r=crypto.randomUUID(),l=this.options.startSession,c=l?void 0:this.options.sessionContext,a=l?void 0:JSON.stringify({chat_session_id:o,input_mode:this.activeInputMode==="push-to-talk"?"push_to_talk":"automatic",...e.overrides?{overrides:e.overrides}:{},...c?.userName?.trim()?{user_name:c.userName}:{},...c?.timeZone?.trim()?{time_zone:c.timeZone}:{}}),u=()=>this.options.startSession?Promise.resolve(this.options.startSession({agentId:this.options.agentId,inputMode:this.activeInputMode,requestId:r,sessionId:o,reportedSource:this.options.reportedSource??"SDK",...e.overrides?{overrides:e.overrides}:{},signal:i.signal})):this.resolveSessionToken().then(v=>fetch(this.apiOrigin+"/v1/agents/"+encodeURIComponent(this.options.agentId)+"/sessions",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json",Authorization:"Bearer "+v,"Idempotency-Key":r,"X-Animated-Waffle-Reported-Source":this.options.reportedSource??"SDK"},signal:i.signal,body:a})),g;try{g=await this.withTimeout(l?u():u().catch(v=>{if(i.signal.aborted)throw v;return u()}),Re,"Timed out creating the Waffle session.",()=>i.abort())}catch(v){let b=C(v,"session","Could not create session.");throw t!==this.lifecycleVersion?new d("The connection attempt was cancelled."):(this.fail(b),b)}finally{this.requestAbort===i&&(this.requestAbort=null)}if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");let k=await g.json().catch(()=>null);if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");if(!g.ok){let v=F(k),b=new d(M(v?.detail)??"Could not create session.",g.status,M(v?.code));throw this.fail(b),b}let f;try{f=dt(k,this.activeInputMode,this.options.agentId,typeof this.options.developerFeedback=="function")}catch(v){let b=C(v,"session","Could not parse the Waffle session.");throw this.fail(b),b}this.currentSession?.id!==f.id&&this.clearDeveloperFeedbackGrant(),this.diagnosticState.update({sessionMs:performance.now()-n}),this.currentSession=f,this.emit("session",oe(f)),N(this,"presentation",f.avatar),this.setStatus("connecting");let y,p,T=new Promise((v,b)=>{y=v,p=b}),P,j=new Promise((v,b)=>{P=b});this.readyWaiter={reject:v=>{p(v),P(v)},resolve:y,version:t};let h=this.room,S=!0,I=()=>S&&t===this.lifecycleVersion&&h===this.room&&this.currentStatus==="connecting",E=async()=>{let v=performance.now();if(await h.connect(f.room,f.token).catch(b=>{throw C(b,"transport","Could not connect to the Agent room.")}),!I())throw await h.disconnect().catch(()=>{}),new d("The connection attempt was cancelled.");if(this.diagnosticState.update({transportMs:performance.now()-v}),this.options.avatarMotion||await h.startAudio().catch(b=>{throw C(b,"audio","Could not start Agent audio.")}),!I())throw new d("The connection attempt was cancelled.");if(this.options.inputAudioEnabled!==!1){this.diagnosticState.update({microphone:"requesting"});let b=await h.localParticipant.setMicrophoneEnabled(!0).catch(W=>{throw new d(W instanceof Error?W.message:"Could not start the microphone.",void 0,"microphone_unavailable","audio",{cause:W})}),D=b?.audioTrack?.mediaStreamTrack;if(!I())throw D?.stop(),await h.disconnect().catch(()=>{}),new d("The connection attempt was cancelled.");if(!b||b.isMuted||b.isUpstreamPaused||!D||!D.enabled||D.muted||D.readyState!=="live")throw new d("The microphone track did not publish.",void 0,"microphone_unavailable","audio");this.diagnosticState.update({microphone:"on"}),this.audioTiming?.microphone(D)}for(let b of h.remoteParticipants.values())this.handleParticipant(b)};try{await Promise.race([Promise.all([E().then(()=>this.withTimeout(T,Ze,"Timed out waiting for the Waffle Agent.",()=>{h.disconnect().catch(()=>{})},"transport")),T]),j])}catch(v){S=!1,this.readyWaiter?.version===t&&(this.readyWaiter=null),this.currentStatus!=="disconnected"&&await h.disconnect().catch(()=>{});let b=C(v,"transport","Could not connect.");throw t!==this.lifecycleVersion?new d("The connection attempt was cancelled."):(this.fail(b),b)}finally{S=!1,this.readyWaiter?.version===t&&(this.readyWaiter=null)}if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");return this.setStatus("connected"),oe(f)}async stopInternal(){if(this.readyWaiter?.reject(new d("The connection attempt was cancelled.")),this.readyWaiter=null,this.requestAbort?.abort(),this.turnId=null,this.turnControl=Promise.resolve(),this.failedStartTurnKey=null,this.agentIdentity=null,this.clearTranscriptProgress(),this.stopAudio(),this.currentStatus!=="disconnected"){this.setStatus("disconnecting");try{await this.room.disconnect()}finally{this.emit("agentSpeaking",!1),this.setStatus("disconnected")}}}sendInputControl(e,t){if(this.activeInputMode!=="push-to-talk")throw new d('Push-to-talk controls require inputMode: "push-to-talk".');this.assertConnected();let n=this.agentIdentity;if(!n)throw new d("The Waffle Agent is not ready.",void 0,"push_to_talk_agent_unavailable","transport");let i=this.room,o=this.turnControl.then(()=>{if(e!=="start_turn"&&this.failedStartTurnKey===t){this.failedStartTurnKey=null;return}return this.performControlRpc(i,n,e,t)});return this.turnControl=o.then(()=>{},r=>{e==="start_turn"&&(this.failedStartTurnKey=t),this.turnId!==null&&String(this.turnId)===t&&(this.turnId=null),this.emit("error",rt(r,e))}),this.turnControl}async performControlRpc(e,t,n,i){let o=JSON.stringify({turnId:i}),r=n==="start_turn"?2:1,l;for(let c=1;c<=r;c+=1)try{return await e.localParticipant.performRpc({destinationIdentity:t,method:n,payload:o})}catch(a){if(l=a,c<r&&nt(a))continue;throw a}throw l}assertPushToTalkAvailable(){if(this.options.inputAudioEnabled===!1)throw new d("Push-to-talk requires input audio to be enabled.",void 0,"input_audio_disabled")}assertConnected(){if(this.status!=="connected")throw new d("Start the Waffle session before sending input.")}async requestDeveloperFeedback(e,t){if(typeof this.options.developerFeedback!="function")throw new d("Developer Feedback is not enabled for this client.",void 0,"developer_feedback_disabled");let n=await this.resolveDeveloperFeedbackGrant(),i;try{i=await fetch(n.endpoint,{method:e,credentials:"omit",cache:"no-store",headers:{Accept:"application/json",Authorization:`Bearer ${n.token}`,...t?{"Content-Type":"application/json"}:{}},...t?{body:JSON.stringify(t)}:{}})}catch{throw new d("Could not save Developer Feedback.",void 0,"developer_feedback_network_error")}let o=await i.json().catch(()=>null);if(!i.ok){let u=F(o);throw new d(M(u?.detail)??"Could not save Developer Feedback.",i.status,M(u?.code))}let r=F(o),l=r?.round_id,c=r?.pending_count,a=r?.dashboard_url;if(l!==null&&(typeof l!="string"||!ie.test(l))||typeof c!="number"||!Number.isSafeInteger(c)||c<0||a!==null&&typeof a!="string")throw new d("Developer Feedback response is invalid.");return typeof a=="string"&&Oe(a,"dashboard_url"),{roundId:l,pendingCount:c,dashboardUrl:a}}resolveDeveloperFeedbackGrant(){let e=this.currentSession,t=this.options.developerFeedback;if(!e||typeof t!="function")throw new d("Start a Developer Feedback-enabled Waffle session first.",void 0,"developer_feedback_unavailable");if(this.developerFeedbackGrant?.sourceSessionId===e.id&&Date.parse(this.developerFeedbackGrant.expiresAt)>Date.now())return Promise.resolve(this.developerFeedbackGrant);if(this.developerFeedbackGrantPromise)return this.developerFeedbackGrantPromise;let n=new AbortController;this.developerFeedbackGrantAbort=n;let i=this.withTimeout(Promise.resolve(t({agentId:e.agentId,sourceSessionId:e.id,signal:n.signal})),Re,"Timed out requesting a Developer Feedback grant.",()=>n.abort()).then(async o=>{let r=await o.json().catch(()=>null);if(!o.ok){let a=F(r);throw new d(M(a?.detail)??"Could not authorize Developer Feedback.",o.status,M(a?.code))}let l=F(r),c={endpoint:Oe(l?.endpoint,"developerFeedback.endpoint"),token:_(l?.token,"developerFeedback.token"),expiresAt:lt(l?.expires_at,"developer_feedback.expires_at"),sourceSessionId:_(l?.source_session_id,"developer_feedback.source_session_id")};if(c.sourceSessionId!==e.id)throw new d("Developer Feedback grant targets a different Session.");if(Date.parse(c.expiresAt)<=Date.now())throw new d("Developer Feedback grant is already expired.");if(this.currentSession?.id!==e.id)throw new d("The Waffle session changed while authorizing feedback.");return this.developerFeedbackGrant=c,c}).finally(()=>{this.developerFeedbackGrantPromise===i&&(this.developerFeedbackGrantPromise=null),this.developerFeedbackGrantAbort===n&&(this.developerFeedbackGrantAbort=null)});return this.developerFeedbackGrantPromise=i,i}handleParticipant(e){if(e.kind===L.AGENT){if(this.agentIdentity=e.identity,this.motionAgentIdentity||this.emit("agentSpeaking",e.attributes["lk.agent.state"]==="speaking"),e.attributes["animated-waffle.runtime.ready"]==="true"){if(this.options.avatarMotion===!0&&(this.motionAgentIdentity!==e.identity||e.attributes["animated-waffle.avatar.motion"]!==V)){this.readyWaiter?.reject(new d("The voice service did not negotiate avatar motion.",void 0,"avatar_motion_unsupported"));return}if(this.options.application&&e.attributes["animated-waffle.application.version"]!=="2"){this.readyWaiter?.reject(new d("The voice service does not support application sessions.",void 0,"application_session_unsupported"));return}this.diagnostics.agentReadyMs===null&&this.diagnostics.connectionState==="connecting"&&this.diagnosticState.update({agentReadyMs:this.diagnostics.sessionMs===null?null:performance.now()-this.connectionStartedAt-this.diagnostics.sessionMs}),this.readyWaiter?.resolve()}for(let t of e.audioTrackPublications.values()){let n=t.track;if(n?.kind===ne.Kind.Audio){this.setAudioTrack(n.mediaStreamTrack),this.audioTiming?.output(n.receiver);break}}}}handleDisconnected(e){this.readyWaiter?.reject(new d(e,void 0,void 0,"transport")),this.readyWaiter=null,this.turnId=null,this.turnControl=Promise.resolve(),this.failedStartTurnKey=null,this.agentIdentity=null,this.clearTranscriptProgress(),this.stopAudio(),this.emit("agentSpeaking",!1),this.setStatus("disconnected")}setAudioTrack(e){if(this.options.avatarMotion||e===this.currentRemoteAudioTrack||(this.stopAudio(!1),this.currentRemoteAudioTrack=e,this.emit("remoteAudioTrack",e),N(this,"audioTrack",e),!e||typeof document>"u")||this.options.audioPlayback===!1)return;let t=document.createElement("audio");t.autoplay=!0,t.muted=this.audioMuted,t.setAttribute("playsinline",""),t.srcObject=new MediaStream([e]),this.audio=t,t.play().catch(n=>{t===this.audio&&this.emit("error",C(n,"audio","Browser blocked agent audio."))})}stopAudioElement(){this.audio&&(this.audio.pause(),this.audio.srcObject=null,this.audio=null)}stopAudio(e=!0){if(this.stopAudioElement(),e){this.audioTiming?.close(),this.audioTiming=null;let t=this.motionSync;this.motionSync=null,t?.dispose(),this.motionPreparing=null,this.motionFeedback=[],this.motionInterruptBarrier?.reject(new Error("Paired playback stopped before interruption was acknowledged.")),this.motionInterruptBarrier=null,this.motionAgentIdentity=null,N(this,"motionMode",!1)}e&&this.currentRemoteAudioTrack&&(this.currentRemoteAudioTrack=null,this.emit("remoteAudioTrack",null),N(this,"audioTrack",null))}waitForMotionParticipant(e,t){return new Promise(n=>{let i=!1,o=()=>{i||(i=!0,clearTimeout(c),e.off(O.ParticipantConnected,r),e.off(O.Disconnected,o),l(),n())},r=a=>{a.identity===t&&o()},l=this.on("connectionState",a=>{a!=="connecting"&&o()}),c=setTimeout(o,8e3);e.on(O.ParticipantConnected,r),e.on(O.Disconnected,o),(e!==this.room||this.currentStatus!=="connecting"||e.remoteParticipants.has(t))&&o()})}prepareMotionSync(){if(this.motionPreparing)return this.motionPreparing;let e=new Q({frame:t=>{t&&this.diagnostics.motion!=="active"&&this.diagnosticState.update({motion:"active"}),N(this,"motion",t)},playback:(t,n,i)=>{this.motionSync===e&&this.audioTiming?.pairedPlayback(t,n,i)},caption:t=>this.emit("transcriptProgress",t),progress:t=>{if(this.motionSync!==e)return;for(let r of t.replies)r.state==="interrupted"&&this.audioTiming?.tracker.interrupted(r.id);this.emit("agentSpeaking",t.state!=="stopped"&&t.replies.some(r=>r.state==="playing"));let n=this.motionFeedback.at(-1),i=r=>JSON.stringify([r.id,r.replies.map(l=>[l.id,l.state])]),o={progress:t,revision:++this.motionFeedbackRevision};if(n&&i(n.progress)===i(t)?this.motionFeedback[this.motionFeedback.length-1]=o:this.motionFeedback.push(o),this.motionFeedback.length>64){this.failMotion(new Error("paired_feedback_overflow"));return}this.motionFeedbackSending||this.sendMotionFeedback().catch(()=>{})},fail:t=>{this.motionSync===e&&this.failMotion(t)}});return e.setMuted(this.audioMuted),this.motionSync=e,this.motionPreparing=e.prepare()}sendMotionFeedback(){if(this.motionFeedbackSending)return this.motionFeedbackSending;let e=this.room,t=this.motionAgentIdentity,n=this.motionSync;if(!t||!n)return Promise.resolve();let o=(async()=>{try{for(;this.motionFeedback.length&&e===this.room&&t===this.motionAgentIdentity&&n===this.motionSync;){let{progress:r,revision:l}=this.motionFeedback.shift();await e.localParticipant.performRpc({destinationIdentity:t,method:ke,payload:JSON.stringify(r),responseTimeout:5e3}),this.motionFeedbackAcknowledged=Math.max(this.motionFeedbackAcknowledged,l),this.motionInterruptBarrier&&this.motionInterruptBarrier.revision<=this.motionFeedbackAcknowledged&&(this.motionInterruptBarrier.resolve(),this.motionInterruptBarrier=null)}}catch(r){if(e===this.room&&n===this.motionSync)throw this.failMotion(r),r}})().finally(()=>{this.motionFeedbackSending===o&&(this.motionFeedbackSending=null,this.motionFeedback.length&&this.sendMotionFeedback().catch(()=>{}))});return this.motionFeedbackSending=o,o}waitForMotionInterruption(){let e=this.motionFeedbackRevision;if(e<=this.motionFeedbackAcknowledged)return Promise.resolve();if(this.motionInterruptBarrier)return this.motionInterruptBarrier.revision=e,this.motionInterruptBarrier.promise;let t,n,i=new Promise((o,r)=>{t=o,n=r});return this.motionInterruptBarrier={revision:e,promise:i,resolve:t,reject:n},this.motionFeedbackSending||this.sendMotionFeedback().catch(()=>{}),i}failMotion(e){if(!this.motionSync)return;let t=this.room;this.fail(new d("Paired audio and avatar playback stopped. Reconnect to resume.",void 0,"avatar_playback_failed","audio",{cause:e})),t.disconnect().catch(()=>{})}completeTranscriptExecution(e){this.completedProgressExecutions.add(e);for(let[t,n]of this.progressText)this.progressExecutions.get(t)===e&&(this.emit("transcriptProgress",{...n,complete:!0}),this.progressExecutions.delete(t),this.progressText.delete(t))}clearTranscriptProgress(){this.progressExecutions.clear(),this.progressText.clear(),this.completedProgressExecutions.clear()}setStatus(e){e==="disconnected"&&(this.recreateRoomOnConnect=!0),e==="disconnected"&&this.currentSession&&(this.clearDeveloperFeedbackGrant(),this.currentSession=null,this.emit("session",null),N(this,"presentation",null)),this.currentStatus!==e&&(this.currentStatus=e,this.emit("connectionState",e),this.emit("status",e))}clearDeveloperFeedbackGrant(){this.developerFeedbackGrantAbort?.abort(),this.developerFeedbackGrantAbort=null,this.developerFeedbackGrantPromise=null,this.developerFeedbackGrant=null}fail(e){let t=e.stage==="audio"&&this.diagnostics.microphone==="requesting";this.readyWaiter?.reject(e),this.readyWaiter=null,this.stopAudio(),this.emit("agentSpeaking",!1),this.setStatus("disconnected"),t&&this.diagnosticState.update({microphone:"error"}),this.emit("error",e)}async resolveSessionToken(){if(!this.options.sessionToken)throw new d("No session token provider is configured.");let e=performance.now(),t=this.lifecycleVersion,n=await this.options.sessionToken();if(t!==this.lifecycleVersion)throw new d("The connection attempt was cancelled.");if(this.diagnosticState.update({tokenMs:performance.now()-e}),!n.trim())throw new d("sessionToken must not be empty.",void 0,"invalid_session_token");return n}withTimeout(e,t,n,i,o="session"){return new Promise((r,l)=>{let c=setTimeout(()=>{i?.(),l(new d(n,void 0,void 0,o))},t);e.then(a=>{clearTimeout(c),r(a)},a=>{clearTimeout(c),l(a)})})}emit(e,t){if(e==="session")this.diagnosticState.session(t);else if(e==="error")this.diagnosticState.failure(t);else if(e==="connectionState"){let n=t;this.diagnosticState.update({connectionState:n,...n==="disconnected"?{microphone:this.options.inputAudioEnabled===!1?"disabled":"off",playback:"idle",motion:"off"}:{}}),n==="connected"&&this.diagnosticState.connected()}else e==="agentSpeaking"&&this.diagnosticState.update({playback:this.options.audioPlayback===!1?"custom":this.audioMuted?"muted":this.diagnostics.playback==="blocked"?"blocked":t?"speaking":"idle"});this.listeners.get(e)?.forEach(n=>{n(t)})}};export{Ne as WaffleClient,d as WaffleError};
@@ -1,3 +1,6 @@
1
+ import { WaffleDiagnostics } from './index.js';
2
+ import 'zod';
3
+
1
4
  interface InternalAvatarEvent {
2
5
  phase: 'emotion' | 'vocal-action';
3
6
  contextId?: string | null;
@@ -28,6 +31,8 @@ interface InternalAvatarPresentation {
28
31
  };
29
32
  }
30
33
  interface InternalWaffleEvents {
34
+ avatarLoad: WaffleDiagnostics['avatar'];
35
+ avatarError: NonNullable<WaffleDiagnostics['error']>;
31
36
  avatar: InternalAvatarEvent;
32
37
  audioTrack: MediaStreamTrack | null;
33
38
  presentation: InternalAvatarPresentation | null;
@@ -35,7 +40,9 @@ interface InternalWaffleEvents {
35
40
  motionMode: boolean;
36
41
  }
37
42
  type InternalListener<K extends keyof InternalWaffleEvents> = (payload: InternalWaffleEvents[K]) => void;
43
+ declare function getInternalMotionMode(owner: object): boolean;
44
+ declare function getInternalPresentation(owner: object): InternalAvatarPresentation | null;
38
45
  declare function subscribeInternal<K extends keyof InternalWaffleEvents>(owner: object, event: K, listener: InternalListener<K>): () => void;
39
46
  declare function emitInternal<K extends keyof InternalWaffleEvents>(owner: object, event: K, payload: InternalWaffleEvents[K]): void;
40
47
 
41
- export { type InternalAvatarEvent, type InternalAvatarMotionFrame, type InternalAvatarPresentation, type InternalWaffleEvents, emitInternal, subscribeInternal };
48
+ export { type InternalAvatarEvent, type InternalAvatarMotionFrame, type InternalAvatarPresentation, type InternalWaffleEvents, emitInternal, getInternalMotionMode, getInternalPresentation, subscribeInternal };
package/dist/internal.js CHANGED
@@ -1 +1 @@
1
- import{a,b}from"./chunk-4OFU2T5E.js";export{b as emitInternal,a as subscribeInternal};
1
+ import{a,b,c,d}from"./chunk-QY6J2GDU.js";export{d as emitInternal,a as getInternalMotionMode,b as getInternalPresentation,c as subscribeInternal};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animated-waffle/client",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Framework-independent browser SDK for Narya Animated Waffle talking Agents.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  "tag": "latest"
36
36
  },
37
37
  "dependencies": {
38
- "livekit-client": "2.21.0",
39
- "zod": "^4.3.6"
38
+ "livekit-client": "2.22.4",
39
+ "zod": "^4.4.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "tsup": "^8.5.0",
@@ -1 +0,0 @@
1
- var o=new WeakMap;function i(n,t,a){let e=o.get(n)??new Map,r=e.get(t)??new Set;return r.add(a),e.set(t,r),o.set(n,e),()=>r.delete(a)}function l(n,t,a){o.get(n)?.get(t)?.forEach(e=>{e(a)})}export{i as a,l as b};