@devicai/ui 0.61.2 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/api/client.js +3 -2
- package/dist/cjs/api/client.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/LiveVoicePanel.js +8 -4
- package/dist/cjs/components/ChatDrawer/LiveVoicePanel.js.map +1 -1
- package/dist/cjs/hooks/useDevicLiveVoice.js +1 -0
- package/dist/cjs/hooks/useDevicLiveVoice.js.map +1 -1
- package/dist/cjs/styles.css +1 -1
- package/dist/cjs/voice/LiveVoiceController.js +98 -2
- package/dist/cjs/voice/LiveVoiceController.js.map +1 -1
- package/dist/esm/api/client.d.ts +2 -1
- package/dist/esm/api/client.js +3 -2
- package/dist/esm/api/client.js.map +1 -1
- package/dist/esm/api/liveVoice.types.d.ts +5 -0
- package/dist/esm/components/ChatDrawer/LiveVoicePanel.js +8 -4
- package/dist/esm/components/ChatDrawer/LiveVoicePanel.js.map +1 -1
- package/dist/esm/hooks/useDevicLiveVoice.d.ts +2 -0
- package/dist/esm/hooks/useDevicLiveVoice.js +1 -0
- package/dist/esm/hooks/useDevicLiveVoice.js.map +1 -1
- package/dist/esm/styles.css +1 -1
- package/dist/esm/voice/LiveVoiceController.d.ts +10 -0
- package/dist/esm/voice/LiveVoiceController.js +98 -2
- package/dist/esm/voice/LiveVoiceController.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ class LiveVoiceController {
|
|
|
12
12
|
this.attempts = 0;
|
|
13
13
|
this.disposeTransport = () => { };
|
|
14
14
|
this.disposed = false;
|
|
15
|
+
/** Last moment anyone spoke, or the person confirmed presence. */
|
|
16
|
+
this.lastActivity = 0;
|
|
15
17
|
}
|
|
16
18
|
update(patch) {
|
|
17
19
|
this.snapshot = { ...this.snapshot, ...patch };
|
|
@@ -31,6 +33,15 @@ class LiveVoiceController {
|
|
|
31
33
|
this.update({ playbackBlocked: true });
|
|
32
34
|
}
|
|
33
35
|
}
|
|
36
|
+
/** The person is still there: restart the idle clock here and on the server. */
|
|
37
|
+
stillHere() {
|
|
38
|
+
this.lastActivity = Date.now();
|
|
39
|
+
if (this.snapshot.idleEndsAt)
|
|
40
|
+
this.update({ idleEndsAt: undefined });
|
|
41
|
+
const id = this.snapshot.sessionId;
|
|
42
|
+
if (id && this.snapshot.state === 'connected')
|
|
43
|
+
void this.client.getLiveSessionStatus(this.assistantId, id, true).catch(() => { });
|
|
44
|
+
}
|
|
34
45
|
async start(chatUid, recovering = false) {
|
|
35
46
|
if (this.disposed || this.closing || (!recovering && !['idle', 'error'].includes(this.snapshot.state)))
|
|
36
47
|
return;
|
|
@@ -39,7 +50,8 @@ class LiveVoiceController {
|
|
|
39
50
|
if (!recovering)
|
|
40
51
|
this.attempts = 0;
|
|
41
52
|
this.update({ state: 'connecting', error: undefined, chatUid, sessionId: undefined,
|
|
42
|
-
seconds: 0, ...(recovering ? {} : { transcript: [], muted: false }) });
|
|
53
|
+
seconds: 0, idleEndsAt: undefined, endReason: undefined, ...(recovering ? {} : { transcript: [], muted: false }) });
|
|
54
|
+
this.lastActivity = Date.now();
|
|
43
55
|
let media;
|
|
44
56
|
let peer;
|
|
45
57
|
let channel;
|
|
@@ -52,10 +64,23 @@ class LiveVoiceController {
|
|
|
52
64
|
let sessionId;
|
|
53
65
|
let connectionLostAt = 0;
|
|
54
66
|
let healthFailures = 0;
|
|
67
|
+
let micCheck;
|
|
68
|
+
let micContext;
|
|
69
|
+
let micAnalyser;
|
|
70
|
+
let micQuiet = 0;
|
|
55
71
|
const cleanup = () => {
|
|
56
72
|
clearInterval(health);
|
|
57
73
|
clearTimeout(deadline);
|
|
58
74
|
clearTimeout(connectDeadline);
|
|
75
|
+
clearInterval(micCheck);
|
|
76
|
+
if (micContext) {
|
|
77
|
+
try {
|
|
78
|
+
void micContext.close().catch(() => { });
|
|
79
|
+
}
|
|
80
|
+
catch { /* Already closed. */ }
|
|
81
|
+
micContext = undefined;
|
|
82
|
+
micAnalyser = undefined;
|
|
83
|
+
}
|
|
59
84
|
media?.getTracks().forEach(track => track.stop());
|
|
60
85
|
if (channel) {
|
|
61
86
|
channel.onclose = null;
|
|
@@ -80,8 +105,64 @@ class LiveVoiceController {
|
|
|
80
105
|
return;
|
|
81
106
|
clearTimeout(connectDeadline);
|
|
82
107
|
media?.getAudioTracks().forEach(track => { track.enabled = !this.snapshot.muted; });
|
|
108
|
+
if (this.snapshot.state !== 'connected') {
|
|
109
|
+
this.lastActivity = Date.now();
|
|
110
|
+
watchMicrophone();
|
|
111
|
+
}
|
|
83
112
|
this.update({ state: 'connected' });
|
|
84
113
|
};
|
|
114
|
+
// A microphone that delivers nothing — ended track, system mute, or a
|
|
115
|
+
// stream of exact digital silence — would otherwise keep a paid call open
|
|
116
|
+
// with nobody able to speak. Analyse only; never route audio.
|
|
117
|
+
const watchMicrophone = () => {
|
|
118
|
+
if (micCheck)
|
|
119
|
+
return;
|
|
120
|
+
const track = media?.getAudioTracks()[0];
|
|
121
|
+
if (!track)
|
|
122
|
+
return;
|
|
123
|
+
const samples = new Uint8Array(256);
|
|
124
|
+
try {
|
|
125
|
+
if (typeof AudioContext !== 'undefined') {
|
|
126
|
+
micContext = new AudioContext();
|
|
127
|
+
micAnalyser = micContext.createAnalyser();
|
|
128
|
+
micAnalyser.fftSize = 256;
|
|
129
|
+
micContext.createMediaStreamSource(media).connect(micAnalyser);
|
|
130
|
+
void micContext.resume().catch(() => { });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
micAnalyser = undefined;
|
|
135
|
+
}
|
|
136
|
+
micCheck = setInterval(() => {
|
|
137
|
+
if (!current() || this.snapshot.state !== 'connected')
|
|
138
|
+
return;
|
|
139
|
+
const fail = () => {
|
|
140
|
+
this.update({ error: new Error('No microphone signal. Check your microphone and start again.') });
|
|
141
|
+
void this.stop();
|
|
142
|
+
};
|
|
143
|
+
if (track.readyState === 'ended') {
|
|
144
|
+
fail();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (this.snapshot.muted) {
|
|
148
|
+
micQuiet = 0;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
let silent = track.muted === true;
|
|
152
|
+
if (micAnalyser && !silent) {
|
|
153
|
+
try {
|
|
154
|
+
micAnalyser.getByteTimeDomainData(samples);
|
|
155
|
+
silent = samples.every(sample => sample === 128);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
silent = false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
micQuiet = silent ? micQuiet + 1 : 0;
|
|
162
|
+
if (micQuiet >= 15)
|
|
163
|
+
fail();
|
|
164
|
+
}, 1000);
|
|
165
|
+
};
|
|
85
166
|
const recover = async () => {
|
|
86
167
|
if (!current())
|
|
87
168
|
return;
|
|
@@ -155,6 +236,9 @@ class LiveVoiceController {
|
|
|
155
236
|
}
|
|
156
237
|
if (['session.input_transcript.delta', 'session.output_transcript.delta'].includes(event.type) && typeof event.delta === 'string') {
|
|
157
238
|
lastTranscript = Date.now();
|
|
239
|
+
this.lastActivity = lastTranscript;
|
|
240
|
+
if (this.snapshot.idleEndsAt)
|
|
241
|
+
this.update({ idleEndsAt: undefined });
|
|
158
242
|
const role = event.type === 'session.input_transcript.delta' ? 'user' : 'assistant';
|
|
159
243
|
const turns = this.snapshot.transcript.slice(-3).map(turn => ({ ...turn }));
|
|
160
244
|
if (turns[turns.length - 1]?.role === role)
|
|
@@ -167,6 +251,8 @@ class LiveVoiceController {
|
|
|
167
251
|
void this.client.getLiveSessionStatus(this.assistantId, sessionId).then(state => {
|
|
168
252
|
if (!current())
|
|
169
253
|
return;
|
|
254
|
+
if (state.endReason)
|
|
255
|
+
this.update({ endReason: state.endReason });
|
|
170
256
|
if (state.restartRequested)
|
|
171
257
|
void recover();
|
|
172
258
|
else
|
|
@@ -211,7 +297,7 @@ class LiveVoiceController {
|
|
|
211
297
|
void this.client.closeLiveSession(this.assistantId, sessionId).catch(() => { });
|
|
212
298
|
return;
|
|
213
299
|
}
|
|
214
|
-
this.update({ sessionId, chatUid: result.chatUid });
|
|
300
|
+
this.update({ sessionId, chatUid: result.chatUid, idleTimeoutSeconds: result.idleTimeoutSeconds || 0 });
|
|
215
301
|
this.created(result.chatUid);
|
|
216
302
|
deadline = setTimeout(() => { void this.stop(); }, Math.max(1, Math.min(6000, result.maxDurationSeconds || 6000)) * 1000);
|
|
217
303
|
await peer.setRemoteDescription({ type: 'answer', sdp: result.sdp });
|
|
@@ -226,6 +312,16 @@ class LiveVoiceController {
|
|
|
226
312
|
return;
|
|
227
313
|
healthFailures = 0;
|
|
228
314
|
this.update({ seconds: state.seconds });
|
|
315
|
+
// The server ends a silent call at idleTimeoutSeconds; warn for the
|
|
316
|
+
// last 30 s (or half the window when it is short) so the person can
|
|
317
|
+
// say "I'm here" instead of losing the call.
|
|
318
|
+
const idle = this.snapshot.idleTimeoutSeconds || 0;
|
|
319
|
+
if (idle > 0 && this.snapshot.state === 'connected') {
|
|
320
|
+
const endsAt = this.lastActivity + idle * 1000;
|
|
321
|
+
const warn = Date.now() >= endsAt - Math.min(30000, idle * 500);
|
|
322
|
+
if (warn ? this.snapshot.idleEndsAt !== endsAt : !!this.snapshot.idleEndsAt)
|
|
323
|
+
this.update({ idleEndsAt: warn ? endsAt : undefined });
|
|
324
|
+
}
|
|
229
325
|
if (state.interrupted || state.restartRequested || (connectionLostAt && Date.now() - connectionLostAt > 5000)) {
|
|
230
326
|
void recover();
|
|
231
327
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveVoiceController.js","sources":["../../../src/voice/LiveVoiceController.ts"],"sourcesContent":["import type { DevicApiClient } from '../api/client';\nimport type { LiveVoiceContext, LiveVoiceTurn } from '../api/liveVoice.types';\n\nexport type LiveVoiceState = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'closing' | 'error';\nexport interface LiveVoiceSnapshot {\n state: LiveVoiceState;\n chatUid?: string;\n sessionId?: string;\n muted: boolean;\n seconds: number;\n transcript: LiveVoiceTurn[];\n input?: MediaStream;\n output?: MediaStream;\n error?: Error;\n playbackBlocked?: boolean;\n}\nexport const initialVoiceSnapshot = (): LiveVoiceSnapshot => ({ state: 'idle', muted: false, seconds: 0, transcript: [] });\n\n/** No browser globals at import/constructor time; loaded only by start(). */\nexport class LiveVoiceController {\n private snapshot = initialVoiceSnapshot();\n private generation = 0;\n private attempts = 0;\n private disposeTransport: () => void = () => {};\n private audio?: HTMLAudioElement;\n private closing?: Promise<void>;\n private disposed = false;\n constructor(private client: DevicApiClient, private assistantId: string,\n private context: LiveVoiceContext, private changed: (value: LiveVoiceSnapshot) => void,\n private created: (chatUid: string) => void) {}\n\n private update(patch: Partial<LiveVoiceSnapshot>) {\n this.snapshot = { ...this.snapshot, ...patch };\n if (!this.disposed) this.changed(this.snapshot);\n }\n mute(muted: boolean) {\n this.snapshot.input?.getAudioTracks().forEach(track => { track.enabled = !muted && this.snapshot.state === 'connected'; });\n this.update({ muted });\n }\n async play() {\n try { await this.audio?.play(); this.update({ playbackBlocked: false }); }\n catch { this.update({ playbackBlocked: true }); }\n }\n async start(chatUid?: string, recovering = false): Promise<void> {\n if (this.disposed || this.closing || (!recovering && !['idle', 'error'].includes(this.snapshot.state))) return;\n const generation = ++this.generation;\n const current = () => generation === this.generation && !this.disposed;\n if (!recovering) this.attempts = 0;\n this.update({ state: 'connecting', error: undefined, chatUid, sessionId: undefined,\n seconds: 0, ...(recovering ? {} : { transcript: [], muted: false }) });\n let media: MediaStream | undefined;\n let peer: RTCPeerConnection | undefined;\n let channel: RTCDataChannel | undefined;\n let audio: HTMLAudioElement | undefined;\n let health: ReturnType<typeof setInterval> | undefined;\n let deadline: ReturnType<typeof setTimeout> | undefined;\n let connectDeadline: ReturnType<typeof setTimeout> | undefined;\n let backendReady = false;\n let lastTranscript = 0;\n let sessionId: string | undefined;\n let connectionLostAt = 0;\n let healthFailures = 0;\n const cleanup = () => {\n clearInterval(health); clearTimeout(deadline); clearTimeout(connectDeadline);\n media?.getTracks().forEach(track => track.stop());\n if (channel) { channel.onclose = null; channel.onmessage = null; channel.close(); }\n if (peer) { peer.onconnectionstatechange = null; peer.ontrack = null; peer.close(); }\n if (audio) { audio.pause(); audio.srcObject = null; }\n this.audio = undefined;\n this.update({ input: undefined, output: undefined });\n };\n this.disposeTransport = cleanup;\n const ready = () => {\n if (!current() || !backendReady || peer?.connectionState !== 'connected') return;\n clearTimeout(connectDeadline);\n media?.getAudioTracks().forEach(track => { track.enabled = !this.snapshot.muted; });\n this.update({ state: 'connected' });\n };\n const recover = async () => {\n if (!current()) return;\n ++this.generation;\n const ticket = this.generation;\n cleanup();\n this.update({ state: 'reconnecting' });\n try {\n if (!sessionId || ++this.attempts > 3) throw new Error('Voice connection interrupted. Please start again.');\n await this.client.closeLiveSession(this.assistantId, sessionId);\n // Never open another paid transport before the old one is confirmed closed.\n for (let i = 0; i < 35 && ticket === this.generation && !this.disposed; i++) {\n const state = await this.client.getLiveSessionStatus(this.assistantId, sessionId);\n if (state.status === 'closed') {\n if (ticket === this.generation && !this.disposed) await this.start(this.snapshot.chatUid, true);\n return;\n }\n await new Promise(resolve => setTimeout(resolve, 2000));\n }\n if (ticket === this.generation) throw new Error('Could not confirm the previous voice session closed.');\n } catch (error) {\n if (ticket === this.generation) this.update({ state: 'error', error: error as Error });\n }\n };\n try {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia || typeof RTCPeerConnection === 'undefined')\n throw new Error('Voice requires microphone access on HTTPS or localhost.');\n media = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true }, video: false });\n if (!current()) { media.getTracks().forEach(track => track.stop()); return; }\n media.getAudioTracks().forEach(track => { track.enabled = false; });\n this.update({ input: media });\n peer = new RTCPeerConnection({ bundlePolicy: 'max-bundle' });\n audio = new Audio(); this.audio = audio; audio.autoplay = true;\n peer.ontrack = event => {\n if (!current()) return;\n const output = new MediaStream([event.track]);\n audio!.srcObject = output;\n this.update({ output }); void this.play();\n };\n peer.onconnectionstatechange = () => {\n ready();\n if (peer?.connectionState === 'disconnected') connectionLostAt ||= Date.now();\n else connectionLostAt = 0;\n if (peer?.connectionState === 'failed') void recover();\n };\n media.getTracks().forEach(track => peer!.addTrack(track, media!));\n channel = peer.createDataChannel('oai-events');\n channel.onmessage = message => {\n if (!current()) return;\n try {\n const event = JSON.parse(message.data);\n if (event.type === 'session.started') { backendReady = true; ready(); }\n if (['session.input_transcript.delta', 'session.output_transcript.delta'].includes(event.type) && typeof event.delta === 'string') {\n lastTranscript = Date.now();\n const role = event.type === 'session.input_transcript.delta' ? 'user' : 'assistant';\n const turns = this.snapshot.transcript.slice(-3).map(turn => ({ ...turn }));\n if (turns[turns.length - 1]?.role === role) turns[turns.length - 1].text = (turns[turns.length - 1].text + event.delta).slice(-2000);\n else turns.push({ role, text: event.delta.slice(-2000) });\n this.update({ transcript: turns });\n }\n if (event.type === 'session.closed' && sessionId) {\n void this.client.getLiveSessionStatus(this.assistantId, sessionId).then(state => {\n if (!current()) return;\n if (state.restartRequested) void recover(); else void this.stop();\n }).catch(() => { if (current()) void this.stop(); });\n }\n if (event.type === 'error') { this.update({ error: new Error('Voice session failed.') }); void this.stop(); }\n } catch { /* Only lifecycle and spoken transcript events are accepted. */ }\n };\n channel.onclose = () => { if (current()) void recover(); };\n connectDeadline = setTimeout(() => {\n if (current()) { this.update({ error: new Error('Voice connection timed out.') }); void this.stop(); }\n }, 30000);\n await peer.setLocalDescription(await peer.createOffer());\n await new Promise<void>((resolve, reject) => {\n if (peer!.iceGatheringState === 'complete') return resolve();\n const done = () => {\n if (peer!.iceGatheringState !== 'complete') return;\n clearTimeout(timer); peer!.removeEventListener('icegatheringstatechange', done); resolve();\n };\n const timer = setTimeout(() => { peer!.removeEventListener('icegatheringstatechange', done); reject(new Error('Could not prepare the audio connection.')); }, 10000);\n peer!.addEventListener('icegatheringstatechange', done);\n });\n if (!current()) return;\n const result = await this.client.createLiveSession(this.assistantId, { ...this.context, chatUid, sdp: peer.localDescription!.sdp });\n sessionId = result.sessionId;\n if (!current()) { void this.client.closeLiveSession(this.assistantId, sessionId).catch(() => {}); return; }\n this.update({ sessionId, chatUid: result.chatUid }); this.created(result.chatUid);\n deadline = setTimeout(() => { void this.stop(); }, Math.max(1, Math.min(6000, result.maxDurationSeconds || 6000)) * 1000);\n await peer.setRemoteDescription({ type: 'answer', sdp: result.sdp });\n let busy = false;\n health = setInterval(async () => {\n if (!current() || busy) return;\n busy = true;\n try {\n const state = await this.client.getLiveSessionStatus(this.assistantId, sessionId!);\n if (!current()) return;\n healthFailures = 0;\n this.update({ seconds: state.seconds });\n if (state.interrupted || state.restartRequested || (connectionLostAt && Date.now() - connectionLostAt > 5000)) { void recover(); return; }\n if (state.status === 'closed') { void this.stop(); return; }\n backendReady = state.connected; ready();\n if (Date.now() - lastTranscript > 1500 && state.transcript?.length)\n this.update({ transcript: state.transcript.slice(-4).filter(t => t.role === 'user' || t.role === 'assistant').map(t => ({ role: t.role, text: t.text.slice(-2000) })) });\n } catch {\n if (current() && ++healthFailures >= 5) { this.update({ error: new Error('Voice status unavailable. Session stopped.')} ); void this.stop(); }\n } finally { busy = false; }\n }, 1000);\n } catch (error) {\n if (!current()) return;\n this.update({ error: error instanceof Error ? error : new Error(String(error)) });\n await this.stop();\n }\n }\n stop(): Promise<void> {\n if (this.closing) return this.closing;\n ++this.generation;\n this.disposeTransport();\n const id = this.snapshot.sessionId;\n this.update({ state: id ? 'closing' : (this.snapshot.error ? 'error' : 'idle') });\n this.closing = (async () => {\n if (id) {\n try { await this.client.closeLiveSession(this.assistantId, id); }\n catch (error) { this.update({ error: error as Error }); }\n }\n this.update({ state: this.snapshot.error ? 'error' : 'idle', sessionId: undefined });\n })().finally(() => { this.closing = undefined; });\n return this.closing;\n }\n dispose() { this.disposed = true; void this.stop(); }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LiveVoiceController.js","sources":["../../../src/voice/LiveVoiceController.ts"],"sourcesContent":["import type { DevicApiClient } from '../api/client';\nimport type { LiveVoiceContext, LiveVoiceTurn } from '../api/liveVoice.types';\n\nexport type LiveVoiceState = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'closing' | 'error';\nexport interface LiveVoiceSnapshot {\n state: LiveVoiceState;\n chatUid?: string;\n sessionId?: string;\n muted: boolean;\n seconds: number;\n transcript: LiveVoiceTurn[];\n input?: MediaStream;\n output?: MediaStream;\n error?: Error;\n playbackBlocked?: boolean;\n /** Seconds of silence after which the server ends the call; 0 = never. */\n idleTimeoutSeconds?: number;\n /** Set while the call is about to end for silence: when it will, so a UI can count down. */\n idleEndsAt?: number;\n /** Why the server ended the last call, e.g. `idle`. */\n endReason?: string;\n}\nexport const initialVoiceSnapshot = (): LiveVoiceSnapshot => ({ state: 'idle', muted: false, seconds: 0, transcript: [] });\n\n/** No browser globals at import/constructor time; loaded only by start(). */\nexport class LiveVoiceController {\n private snapshot = initialVoiceSnapshot();\n private generation = 0;\n private attempts = 0;\n private disposeTransport: () => void = () => {};\n private audio?: HTMLAudioElement;\n private closing?: Promise<void>;\n private disposed = false;\n /** Last moment anyone spoke, or the person confirmed presence. */\n private lastActivity = 0;\n constructor(private client: DevicApiClient, private assistantId: string,\n private context: LiveVoiceContext, private changed: (value: LiveVoiceSnapshot) => void,\n private created: (chatUid: string) => void) {}\n\n private update(patch: Partial<LiveVoiceSnapshot>) {\n this.snapshot = { ...this.snapshot, ...patch };\n if (!this.disposed) this.changed(this.snapshot);\n }\n mute(muted: boolean) {\n this.snapshot.input?.getAudioTracks().forEach(track => { track.enabled = !muted && this.snapshot.state === 'connected'; });\n this.update({ muted });\n }\n async play() {\n try { await this.audio?.play(); this.update({ playbackBlocked: false }); }\n catch { this.update({ playbackBlocked: true }); }\n }\n /** The person is still there: restart the idle clock here and on the server. */\n stillHere() {\n this.lastActivity = Date.now();\n if (this.snapshot.idleEndsAt) this.update({ idleEndsAt: undefined });\n const id = this.snapshot.sessionId;\n if (id && this.snapshot.state === 'connected') void this.client.getLiveSessionStatus(this.assistantId, id, true).catch(() => {});\n }\n async start(chatUid?: string, recovering = false): Promise<void> {\n if (this.disposed || this.closing || (!recovering && !['idle', 'error'].includes(this.snapshot.state))) return;\n const generation = ++this.generation;\n const current = () => generation === this.generation && !this.disposed;\n if (!recovering) this.attempts = 0;\n this.update({ state: 'connecting', error: undefined, chatUid, sessionId: undefined,\n seconds: 0, idleEndsAt: undefined, endReason: undefined, ...(recovering ? {} : { transcript: [], muted: false }) });\n this.lastActivity = Date.now();\n let media: MediaStream | undefined;\n let peer: RTCPeerConnection | undefined;\n let channel: RTCDataChannel | undefined;\n let audio: HTMLAudioElement | undefined;\n let health: ReturnType<typeof setInterval> | undefined;\n let deadline: ReturnType<typeof setTimeout> | undefined;\n let connectDeadline: ReturnType<typeof setTimeout> | undefined;\n let backendReady = false;\n let lastTranscript = 0;\n let sessionId: string | undefined;\n let connectionLostAt = 0;\n let healthFailures = 0;\n let micCheck: ReturnType<typeof setInterval> | undefined;\n let micContext: AudioContext | undefined;\n let micAnalyser: AnalyserNode | undefined;\n let micQuiet = 0;\n const cleanup = () => {\n clearInterval(health); clearTimeout(deadline); clearTimeout(connectDeadline); clearInterval(micCheck);\n if (micContext) { try { void micContext.close().catch(() => {}); } catch { /* Already closed. */ } micContext = undefined; micAnalyser = undefined; }\n media?.getTracks().forEach(track => track.stop());\n if (channel) { channel.onclose = null; channel.onmessage = null; channel.close(); }\n if (peer) { peer.onconnectionstatechange = null; peer.ontrack = null; peer.close(); }\n if (audio) { audio.pause(); audio.srcObject = null; }\n this.audio = undefined;\n this.update({ input: undefined, output: undefined });\n };\n this.disposeTransport = cleanup;\n const ready = () => {\n if (!current() || !backendReady || peer?.connectionState !== 'connected') return;\n clearTimeout(connectDeadline);\n media?.getAudioTracks().forEach(track => { track.enabled = !this.snapshot.muted; });\n if (this.snapshot.state !== 'connected') { this.lastActivity = Date.now(); watchMicrophone(); }\n this.update({ state: 'connected' });\n };\n // A microphone that delivers nothing — ended track, system mute, or a\n // stream of exact digital silence — would otherwise keep a paid call open\n // with nobody able to speak. Analyse only; never route audio.\n const watchMicrophone = () => {\n if (micCheck) return;\n const track = media?.getAudioTracks()[0];\n if (!track) return;\n const samples = new Uint8Array(256);\n try {\n if (typeof AudioContext !== 'undefined') {\n micContext = new AudioContext(); micAnalyser = micContext.createAnalyser(); micAnalyser.fftSize = 256;\n micContext.createMediaStreamSource(media!).connect(micAnalyser); void micContext.resume().catch(() => {});\n }\n } catch { micAnalyser = undefined; }\n micCheck = setInterval(() => {\n if (!current() || this.snapshot.state !== 'connected') return;\n const fail = () => {\n this.update({ error: new Error('No microphone signal. Check your microphone and start again.') });\n void this.stop();\n };\n if (track.readyState === 'ended') { fail(); return; }\n if (this.snapshot.muted) { micQuiet = 0; return; }\n let silent = track.muted === true;\n if (micAnalyser && !silent) {\n try {\n micAnalyser.getByteTimeDomainData(samples);\n silent = samples.every(sample => sample === 128);\n } catch { silent = false; }\n }\n micQuiet = silent ? micQuiet + 1 : 0;\n if (micQuiet >= 15) fail();\n }, 1000);\n };\n const recover = async () => {\n if (!current()) return;\n ++this.generation;\n const ticket = this.generation;\n cleanup();\n this.update({ state: 'reconnecting' });\n try {\n if (!sessionId || ++this.attempts > 3) throw new Error('Voice connection interrupted. Please start again.');\n await this.client.closeLiveSession(this.assistantId, sessionId);\n // Never open another paid transport before the old one is confirmed closed.\n for (let i = 0; i < 35 && ticket === this.generation && !this.disposed; i++) {\n const state = await this.client.getLiveSessionStatus(this.assistantId, sessionId);\n if (state.status === 'closed') {\n if (ticket === this.generation && !this.disposed) await this.start(this.snapshot.chatUid, true);\n return;\n }\n await new Promise(resolve => setTimeout(resolve, 2000));\n }\n if (ticket === this.generation) throw new Error('Could not confirm the previous voice session closed.');\n } catch (error) {\n if (ticket === this.generation) this.update({ state: 'error', error: error as Error });\n }\n };\n try {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia || typeof RTCPeerConnection === 'undefined')\n throw new Error('Voice requires microphone access on HTTPS or localhost.');\n media = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true }, video: false });\n if (!current()) { media.getTracks().forEach(track => track.stop()); return; }\n media.getAudioTracks().forEach(track => { track.enabled = false; });\n this.update({ input: media });\n peer = new RTCPeerConnection({ bundlePolicy: 'max-bundle' });\n audio = new Audio(); this.audio = audio; audio.autoplay = true;\n peer.ontrack = event => {\n if (!current()) return;\n const output = new MediaStream([event.track]);\n audio!.srcObject = output;\n this.update({ output }); void this.play();\n };\n peer.onconnectionstatechange = () => {\n ready();\n if (peer?.connectionState === 'disconnected') connectionLostAt ||= Date.now();\n else connectionLostAt = 0;\n if (peer?.connectionState === 'failed') void recover();\n };\n media.getTracks().forEach(track => peer!.addTrack(track, media!));\n channel = peer.createDataChannel('oai-events');\n channel.onmessage = message => {\n if (!current()) return;\n try {\n const event = JSON.parse(message.data);\n if (event.type === 'session.started') { backendReady = true; ready(); }\n if (['session.input_transcript.delta', 'session.output_transcript.delta'].includes(event.type) && typeof event.delta === 'string') {\n lastTranscript = Date.now(); this.lastActivity = lastTranscript;\n if (this.snapshot.idleEndsAt) this.update({ idleEndsAt: undefined });\n const role = event.type === 'session.input_transcript.delta' ? 'user' : 'assistant';\n const turns = this.snapshot.transcript.slice(-3).map(turn => ({ ...turn }));\n if (turns[turns.length - 1]?.role === role) turns[turns.length - 1].text = (turns[turns.length - 1].text + event.delta).slice(-2000);\n else turns.push({ role, text: event.delta.slice(-2000) });\n this.update({ transcript: turns });\n }\n if (event.type === 'session.closed' && sessionId) {\n void this.client.getLiveSessionStatus(this.assistantId, sessionId).then(state => {\n if (!current()) return;\n if (state.endReason) this.update({ endReason: state.endReason });\n if (state.restartRequested) void recover(); else void this.stop();\n }).catch(() => { if (current()) void this.stop(); });\n }\n if (event.type === 'error') { this.update({ error: new Error('Voice session failed.') }); void this.stop(); }\n } catch { /* Only lifecycle and spoken transcript events are accepted. */ }\n };\n channel.onclose = () => { if (current()) void recover(); };\n connectDeadline = setTimeout(() => {\n if (current()) { this.update({ error: new Error('Voice connection timed out.') }); void this.stop(); }\n }, 30000);\n await peer.setLocalDescription(await peer.createOffer());\n await new Promise<void>((resolve, reject) => {\n if (peer!.iceGatheringState === 'complete') return resolve();\n const done = () => {\n if (peer!.iceGatheringState !== 'complete') return;\n clearTimeout(timer); peer!.removeEventListener('icegatheringstatechange', done); resolve();\n };\n const timer = setTimeout(() => { peer!.removeEventListener('icegatheringstatechange', done); reject(new Error('Could not prepare the audio connection.')); }, 10000);\n peer!.addEventListener('icegatheringstatechange', done);\n });\n if (!current()) return;\n const result = await this.client.createLiveSession(this.assistantId, { ...this.context, chatUid, sdp: peer.localDescription!.sdp });\n sessionId = result.sessionId;\n if (!current()) { void this.client.closeLiveSession(this.assistantId, sessionId).catch(() => {}); return; }\n this.update({ sessionId, chatUid: result.chatUid, idleTimeoutSeconds: result.idleTimeoutSeconds || 0 }); this.created(result.chatUid);\n deadline = setTimeout(() => { void this.stop(); }, Math.max(1, Math.min(6000, result.maxDurationSeconds || 6000)) * 1000);\n await peer.setRemoteDescription({ type: 'answer', sdp: result.sdp });\n let busy = false;\n health = setInterval(async () => {\n if (!current() || busy) return;\n busy = true;\n try {\n const state = await this.client.getLiveSessionStatus(this.assistantId, sessionId!);\n if (!current()) return;\n healthFailures = 0;\n this.update({ seconds: state.seconds });\n // The server ends a silent call at idleTimeoutSeconds; warn for the\n // last 30 s (or half the window when it is short) so the person can\n // say \"I'm here\" instead of losing the call.\n const idle = this.snapshot.idleTimeoutSeconds || 0;\n if (idle > 0 && this.snapshot.state === 'connected') {\n const endsAt = this.lastActivity + idle * 1000;\n const warn = Date.now() >= endsAt - Math.min(30000, idle * 500);\n if (warn ? this.snapshot.idleEndsAt !== endsAt : !!this.snapshot.idleEndsAt) this.update({ idleEndsAt: warn ? endsAt : undefined });\n }\n if (state.interrupted || state.restartRequested || (connectionLostAt && Date.now() - connectionLostAt > 5000)) { void recover(); return; }\n if (state.status === 'closed') { void this.stop(); return; }\n backendReady = state.connected; ready();\n if (Date.now() - lastTranscript > 1500 && state.transcript?.length)\n this.update({ transcript: state.transcript.slice(-4).filter(t => t.role === 'user' || t.role === 'assistant').map(t => ({ role: t.role, text: t.text.slice(-2000) })) });\n } catch {\n if (current() && ++healthFailures >= 5) { this.update({ error: new Error('Voice status unavailable. Session stopped.')} ); void this.stop(); }\n } finally { busy = false; }\n }, 1000);\n } catch (error) {\n if (!current()) return;\n this.update({ error: error instanceof Error ? error : new Error(String(error)) });\n await this.stop();\n }\n }\n stop(): Promise<void> {\n if (this.closing) return this.closing;\n ++this.generation;\n this.disposeTransport();\n const id = this.snapshot.sessionId;\n this.update({ state: id ? 'closing' : (this.snapshot.error ? 'error' : 'idle') });\n this.closing = (async () => {\n if (id) {\n try { await this.client.closeLiveSession(this.assistantId, id); }\n catch (error) { this.update({ error: error as Error }); }\n }\n this.update({ state: this.snapshot.error ? 'error' : 'idle', sessionId: undefined });\n })().finally(() => { this.closing = undefined; });\n return this.closing;\n }\n dispose() { this.disposed = true; void this.stop(); }\n}\n"],"names":[],"mappings":"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"}
|