@shogo-ai/voice 1.10.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +89 -0
  2. package/dist/{chunk-S3S2RNO6.js → chunk-BI6SL35K.js} +1 -184
  3. package/dist/chunk-CLF25CZC.js +987 -0
  4. package/dist/chunk-KMSL5PWK.js +540 -0
  5. package/dist/chunk-ME3SIZZU.js +581 -0
  6. package/dist/chunk-TQOBJMEK.js +17 -0
  7. package/dist/{chunk-T3T4EBWY.js → chunk-UH75UVM2.js} +6 -2
  8. package/dist/chunk-UW7VQ3S6.js +148 -0
  9. package/dist/chunk-XE4LLC7Z.js +1067 -0
  10. package/dist/chunk-ZAW6F3YT.js +172 -0
  11. package/dist/index.cjs +735 -25
  12. package/dist/index.d.cts +373 -3
  13. package/dist/index.d.ts +373 -3
  14. package/dist/index.js +59 -3
  15. package/dist/live/client.cjs +173 -0
  16. package/dist/live/client.d.cts +132 -0
  17. package/dist/live/client.d.ts +132 -0
  18. package/dist/live/client.js +9 -0
  19. package/dist/native/index.d.cts +5 -2
  20. package/dist/native/index.d.ts +5 -2
  21. package/dist/native/index.js +15 -11
  22. package/dist/{organicParticlesShaders-DIva0vBO.d.ts → organicParticlesShaders-seZq7jYX.d.cts} +1 -421
  23. package/dist/{organicParticlesShaders-DIva0vBO.d.cts → organicParticlesShaders-seZq7jYX.d.ts} +1 -421
  24. package/dist/particlesConfig-0YbdPOdY.d.cts +223 -0
  25. package/dist/particlesConfig-0YbdPOdY.d.ts +223 -0
  26. package/dist/react/conversation.cjs +789 -0
  27. package/dist/react/conversation.d.cts +134 -0
  28. package/dist/react/conversation.d.ts +134 -0
  29. package/dist/react/conversation.js +13 -0
  30. package/dist/react/index.cjs +374 -36
  31. package/dist/react/index.d.cts +10 -141
  32. package/dist/react/index.d.ts +10 -141
  33. package/dist/react/index.js +23 -770
  34. package/dist/react/visuals.cjs +1048 -0
  35. package/dist/react/visuals.d.cts +70 -0
  36. package/dist/react/visuals.d.ts +70 -0
  37. package/dist/react/visuals.js +10 -0
  38. package/dist/route/agent.cjs +1038 -85
  39. package/dist/route/agent.js +3 -3
  40. package/dist/route/audio-tags.cjs +1038 -85
  41. package/dist/route/audio-tags.js +3 -3
  42. package/dist/route/index.cjs +1042 -85
  43. package/dist/route/index.d.cts +19 -3
  44. package/dist/route/index.d.ts +19 -3
  45. package/dist/route/index.js +3 -3
  46. package/dist/route/music.cjs +1038 -85
  47. package/dist/route/music.js +3 -3
  48. package/dist/route/op.cjs +2118 -0
  49. package/dist/route/op.d.cts +43 -0
  50. package/dist/route/op.d.ts +43 -0
  51. package/dist/route/op.js +27 -0
  52. package/dist/route/signed-url.cjs +1038 -85
  53. package/dist/route/signed-url.js +3 -3
  54. package/dist/route/tts-preview.cjs +1038 -85
  55. package/dist/route/tts-preview.d.cts +6 -4
  56. package/dist/route/tts-preview.d.ts +6 -4
  57. package/dist/route/tts-preview.js +3 -3
  58. package/dist/server.cjs +1038 -85
  59. package/dist/server.d.cts +40 -8
  60. package/dist/server.d.ts +40 -8
  61. package/dist/server.js +2 -2
  62. package/dist/types-Bfhu-aQH.d.cts +199 -0
  63. package/dist/types-Bfhu-aQH.d.ts +199 -0
  64. package/dist/{types-W9y6eMj3.d.ts → types-CadjTXNH.d.cts} +193 -12
  65. package/dist/{types-W9y6eMj3.d.cts → types-CadjTXNH.d.ts} +193 -12
  66. package/package.json +21 -1
  67. package/dist/chunk-B4JC3DK2.js +0 -529
  68. package/dist/chunk-O362EJQR.js +0 -524
@@ -0,0 +1,581 @@
1
+ import {
2
+ appendTranscriptLine,
3
+ buildSessionPayload,
4
+ createMemoryAddTool,
5
+ createMemoryContextInjector,
6
+ createPostJson,
7
+ createTranscriptDisconnectHandler,
8
+ fetchSignedUrl,
9
+ withProjectQuery
10
+ } from "./chunk-ZAW6F3YT.js";
11
+
12
+ // src/react/useVoiceConversation.ts
13
+ import { useCallback, useEffect, useRef, useState } from "react";
14
+ import { useConversation } from "@elevenlabs/react";
15
+
16
+ // src/react/mic-access.ts
17
+ var MIC_BLOCKED_MESSAGE = "Microphone access is blocked. Allow it in your system or browser settings (on macOS: System Settings > Privacy & Security > Microphone), then try again.";
18
+ var MIC_NOT_FOUND_MESSAGE = "No microphone was found. Connect one and try again.";
19
+ async function requestMicStream(constraints = { audio: true }) {
20
+ const desktop = globalThis.shogoDesktop;
21
+ if (desktop?.isDesktop && desktop.ensureMicAccess) {
22
+ const access = await desktop.ensureMicAccess().catch(() => "granted");
23
+ if (access !== "granted") throw new Error(MIC_BLOCKED_MESSAGE);
24
+ }
25
+ try {
26
+ return await navigator.mediaDevices.getUserMedia(constraints);
27
+ } catch (err) {
28
+ const name = err?.name;
29
+ if (name === "NotAllowedError" || name === "SecurityError") throw new Error(MIC_BLOCKED_MESSAGE);
30
+ if (name === "NotFoundError" || name === "OverconstrainedError") throw new Error(MIC_NOT_FOUND_MESSAGE);
31
+ throw err;
32
+ }
33
+ }
34
+
35
+ // src/react/useVoiceConversation.ts
36
+ function useVoiceConversation(options) {
37
+ const {
38
+ characterName,
39
+ signedUrlPath = "/api/voice/signed-url",
40
+ memoryAddPath = "/api/memory/add",
41
+ memoryRetrievePath = "/api/memory/retrieve",
42
+ transcriptIngestPath = "/api/memory/ingest",
43
+ autoInjectMemory = true,
44
+ includeMemoryTool = true,
45
+ clientTools = {},
46
+ onTranscript,
47
+ shogoApiKey,
48
+ projectId,
49
+ agentName,
50
+ fetchCredentials = shogoApiKey ? "omit" : "same-origin",
51
+ onError,
52
+ onMessage,
53
+ conversationId: optionConversationId,
54
+ dynamicVariables
55
+ } = options;
56
+ const authHeaders = useCallback(() => {
57
+ return shogoApiKey ? { authorization: `Bearer ${shogoApiKey}` } : {};
58
+ }, [shogoApiKey]);
59
+ const resolvedSignedUrlPath = withProjectQuery(signedUrlPath, { projectId, agentName });
60
+ const transcriptRef = useRef([]);
61
+ const lastInjectedRef = useRef("");
62
+ const weStartedSessionRef = useRef(false);
63
+ const [convaiConversationId, setConvaiConversationId] = useState(null);
64
+ const isRestartingRef = useRef(false);
65
+ const conversationRef = useRef(null);
66
+ const postJson = useCallback(
67
+ createPostJson({ authHeaders, fetchCredentials }),
68
+ [fetchCredentials, authHeaders]
69
+ );
70
+ const injectMemoryContext = useCallback(
71
+ createMemoryContextInjector({
72
+ postJson,
73
+ memoryRetrievePath,
74
+ conversationRef,
75
+ enabled: autoInjectMemory,
76
+ lastInjectedRef
77
+ }),
78
+ [autoInjectMemory, memoryRetrievePath, postJson]
79
+ );
80
+ const handleTranscriptOnDisconnect = useCallback(
81
+ createTranscriptDisconnectHandler({
82
+ isRestartingRef,
83
+ weStartedSessionRef,
84
+ transcriptRef,
85
+ onTranscript,
86
+ transcriptIngestPath,
87
+ postJson
88
+ }),
89
+ [onTranscript, postJson, transcriptIngestPath]
90
+ );
91
+ const builtInTools = includeMemoryTool ? { add_memory: createMemoryAddTool({ postJson, memoryAddPath }) } : {};
92
+ const mergedClientTools = { ...builtInTools, ...clientTools };
93
+ const conversation = useConversation({
94
+ clientTools: mergedClientTools,
95
+ onConnect: (info) => {
96
+ const id = info?.conversationId;
97
+ if (typeof id === "string" && id.length > 0) {
98
+ setConvaiConversationId(id);
99
+ }
100
+ if (!isRestartingRef.current) {
101
+ transcriptRef.current = [];
102
+ lastInjectedRef.current = "";
103
+ }
104
+ },
105
+ onDisconnect: () => {
106
+ handleTranscriptOnDisconnect();
107
+ if (!isRestartingRef.current) {
108
+ setConvaiConversationId(null);
109
+ }
110
+ },
111
+ onError: (e) => {
112
+ onError?.(e);
113
+ },
114
+ onMessage: (m) => {
115
+ const msg = m;
116
+ if (!msg?.message || !msg?.source) return;
117
+ appendTranscriptLine(msg, { transcriptRef, weStartedSessionRef });
118
+ onMessage?.({ source: msg.source, message: msg.message });
119
+ if (autoInjectMemory && msg.source === "user" && msg.message.trim().length >= 3 && weStartedSessionRef.current) {
120
+ void injectMemoryContext(msg.message);
121
+ }
122
+ }
123
+ });
124
+ const { status, isSpeaking, isListening, isMuted, setMuted, startSession, endSession } = conversation;
125
+ conversationRef.current = conversation;
126
+ useEffect(() => {
127
+ const onHide = () => {
128
+ if (!weStartedSessionRef.current || transcriptRef.current.length === 0) return;
129
+ const transcript = transcriptRef.current.join("\n").trim();
130
+ transcriptRef.current = [];
131
+ if (transcript.length < 20) return;
132
+ try {
133
+ const blob = new Blob([JSON.stringify({ transcript })], { type: "application/json" });
134
+ navigator.sendBeacon(transcriptIngestPath, blob);
135
+ } catch {
136
+ }
137
+ };
138
+ window.addEventListener("pagehide", onHide);
139
+ return () => window.removeEventListener("pagehide", onHide);
140
+ }, [transcriptIngestPath]);
141
+ const startInternal = useCallback(
142
+ async (opts) => {
143
+ if (status === "connected" || status === "connecting") {
144
+ try {
145
+ endSession();
146
+ await new Promise((r) => setTimeout(r, 120));
147
+ } catch {
148
+ }
149
+ }
150
+ const micStream = await requestMicStream({ audio: true });
151
+ micStream.getTracks().forEach((track) => track.stop());
152
+ const data = await fetchSignedUrl({
153
+ path: resolvedSignedUrlPath,
154
+ fetchCredentials,
155
+ authHeaders
156
+ });
157
+ const ctx = data.userContext || "No prior memories yet.";
158
+ weStartedSessionRef.current = true;
159
+ const sessionPayload = buildSessionPayload({
160
+ signedUrl: data.signedUrl,
161
+ characterName,
162
+ userContext: ctx,
163
+ agentPromptOverride: data.agentPromptOverride,
164
+ suppressFirstMessage: opts?.suppressFirstMessage,
165
+ conversationId: optionConversationId,
166
+ dynamicVariables
167
+ });
168
+ await startSession(sessionPayload);
169
+ },
170
+ [
171
+ status,
172
+ endSession,
173
+ resolvedSignedUrlPath,
174
+ fetchCredentials,
175
+ dynamicVariables,
176
+ startSession,
177
+ characterName,
178
+ authHeaders,
179
+ optionConversationId
180
+ ]
181
+ );
182
+ const start = useCallback(
183
+ async (options2) => {
184
+ await startInternal(options2);
185
+ },
186
+ [startInternal]
187
+ );
188
+ const end = useCallback(() => {
189
+ endSession();
190
+ }, [endSession]);
191
+ const restart = useCallback(
192
+ async (options2) => {
193
+ const suppress = options2?.suppressFirstMessage ?? true;
194
+ isRestartingRef.current = true;
195
+ try {
196
+ try {
197
+ endSession();
198
+ } catch {
199
+ }
200
+ await new Promise((r) => setTimeout(r, 120));
201
+ await startInternal({ suppressFirstMessage: suppress });
202
+ } finally {
203
+ isRestartingRef.current = false;
204
+ }
205
+ },
206
+ [endSession, startInternal]
207
+ );
208
+ const sendContextualUpdate = useCallback((text) => {
209
+ conversationRef.current?.sendContextualUpdate(text);
210
+ }, []);
211
+ const sendUserMessage = useCallback((text) => {
212
+ const ref = conversationRef.current;
213
+ if (!ref) return;
214
+ if (typeof ref.sendUserMessage === "function") {
215
+ ref.sendUserMessage(text);
216
+ return;
217
+ }
218
+ ref.sendContextualUpdate(text);
219
+ try {
220
+ ref.sendUserActivity?.();
221
+ } catch {
222
+ }
223
+ }, []);
224
+ const sendUserActivity = useCallback(() => {
225
+ conversationRef.current?.sendUserActivity?.();
226
+ }, []);
227
+ const getOutputByteFrequencyData = useCallback(() => {
228
+ try {
229
+ const fn = conversation.getOutputByteFrequencyData;
230
+ return fn ? fn() : null;
231
+ } catch {
232
+ return null;
233
+ }
234
+ }, [conversation]);
235
+ return {
236
+ start,
237
+ end,
238
+ restart,
239
+ status,
240
+ isSpeaking,
241
+ isListening,
242
+ isMuted,
243
+ setMuted,
244
+ getOutputByteFrequencyData,
245
+ sendContextualUpdate,
246
+ sendUserMessage,
247
+ sendUserActivity,
248
+ conversationId: optionConversationId ?? convaiConversationId,
249
+ convaiConversationId
250
+ };
251
+ }
252
+
253
+ // src/react/useLiveVoiceConversation.ts
254
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
255
+ function parseEvent(data) {
256
+ try {
257
+ const text = typeof data === "string" ? data : String(data);
258
+ const event = JSON.parse(text);
259
+ return event && typeof event === "object" ? event : null;
260
+ } catch {
261
+ return null;
262
+ }
263
+ }
264
+ function waitForIceGathering(pc) {
265
+ if (pc.iceGatheringState === "complete") return Promise.resolve();
266
+ return new Promise((resolve) => {
267
+ const onStateChange = () => {
268
+ if (pc.iceGatheringState === "complete") {
269
+ pc.removeEventListener("icegatheringstatechange", onStateChange);
270
+ resolve();
271
+ }
272
+ };
273
+ pc.addEventListener("icegatheringstatechange", onStateChange);
274
+ window.setTimeout(() => {
275
+ pc.removeEventListener("icegatheringstatechange", onStateChange);
276
+ resolve();
277
+ }, 2e3);
278
+ });
279
+ }
280
+ function splitContext(text) {
281
+ const chunks = [];
282
+ for (let offset = 0; offset < text.length; offset += 1800) {
283
+ chunks.push(text.slice(offset, offset + 1800));
284
+ }
285
+ return chunks.length ? chunks : [""];
286
+ }
287
+ var TRANSCRIPT_HISTORY_MAX = 30;
288
+ function useLiveVoiceConversation(options) {
289
+ const { mintSession } = options;
290
+ const [status, setStatus] = useState2("disconnected");
291
+ const [isSpeaking, setIsSpeaking] = useState2(false);
292
+ const [isListening, setIsListening] = useState2(false);
293
+ const [isMuted, setIsMuted] = useState2(false);
294
+ const [sessionId, setSessionId] = useState2(null);
295
+ const optionsRef = useRef2(options);
296
+ optionsRef.current = options;
297
+ const pcRef = useRef2(null);
298
+ const dataChannelRef = useRef2(null);
299
+ const mediaStreamRef = useRef2(null);
300
+ const audioRef = useRef2(null);
301
+ const analyserRef = useRef2(null);
302
+ const frequencyRef = useRef2(null);
303
+ const audioContextRef = useRef2(null);
304
+ const speakingFrameRef = useRef2(null);
305
+ const inputTranscriptRef = useRef2("");
306
+ const outputTranscriptRef = useRef2("");
307
+ const historyRef = useRef2([]);
308
+ const closingRef = useRef2(false);
309
+ const mutedRef = useRef2(false);
310
+ const emit = useCallback2((source, message) => {
311
+ const text = message.trim();
312
+ if (!text) return;
313
+ const history = historyRef.current;
314
+ history.push({ role: source === "user" ? "user" : "assistant", text });
315
+ if (history.length > TRANSCRIPT_HISTORY_MAX) history.splice(0, history.length - TRANSCRIPT_HISTORY_MAX);
316
+ optionsRef.current.onMessage?.({ source, message: text });
317
+ }, []);
318
+ const flushTranscripts = useCallback2(() => {
319
+ const input = inputTranscriptRef.current.trim();
320
+ const output = outputTranscriptRef.current.trim();
321
+ inputTranscriptRef.current = "";
322
+ outputTranscriptRef.current = "";
323
+ if (input) emit("user", input);
324
+ if (output) emit("agent", output);
325
+ }, [emit]);
326
+ const sendEvent = useCallback2((event) => {
327
+ const channel = dataChannelRef.current;
328
+ if (channel?.readyState === "open") channel.send(JSON.stringify(event));
329
+ }, []);
330
+ const updateSpeaking = useCallback2(() => {
331
+ const analyser = analyserRef.current;
332
+ if (analyser) {
333
+ const data = frequencyRef.current ?? new Uint8Array(analyser.frequencyBinCount);
334
+ frequencyRef.current = data;
335
+ analyser.getByteFrequencyData(data);
336
+ let sum = 0;
337
+ for (const value of data) sum += value;
338
+ setIsSpeaking(data.length > 0 && sum / data.length > 8);
339
+ }
340
+ if (pcRef.current) speakingFrameRef.current = requestAnimationFrame(updateSpeaking);
341
+ }, []);
342
+ const handleEvent = useCallback2(async (event) => {
343
+ const nested = event.type === "response.event" && event.event ? event.event : event;
344
+ const type = nested.type ?? "";
345
+ if (type === "session.started" || type === "session.created") {
346
+ setStatus("connected");
347
+ setIsListening(!mutedRef.current);
348
+ const id = typeof nested.session?.id === "string" ? nested.session.id : null;
349
+ if (id) setSessionId(id);
350
+ }
351
+ if (type === "session.closed") {
352
+ flushTranscripts();
353
+ setStatus("disconnected");
354
+ setIsListening(false);
355
+ }
356
+ if (type === "error") {
357
+ optionsRef.current.onError?.(new Error(event.error?.message || "GPT-Live error"));
358
+ }
359
+ if (type === "session.input_transcript.delta" || type === "input_audio_transcription.delta") {
360
+ inputTranscriptRef.current += event.delta ?? "";
361
+ }
362
+ if (type === "session.output_transcript.delta" || type === "response.audio_transcript.delta") {
363
+ outputTranscriptRef.current += event.delta ?? "";
364
+ }
365
+ if (type === "session.input_transcript.done" || type === "input_audio_transcription.completed" || type === "session.output_transcript.done" || type === "response.audio_transcript.done" || type === "response.done" || type === "response.completed") {
366
+ flushTranscripts();
367
+ }
368
+ if (type === "session.delegation.created" && nested.delegation?.target !== "responses" && typeof nested.delegation?.id === "string") {
369
+ const onDelegation = optionsRef.current.onDelegation;
370
+ if (!onDelegation) return;
371
+ const delegationId = nested.delegation.id;
372
+ flushTranscripts();
373
+ let result;
374
+ try {
375
+ result = await onDelegation({ delegationId, transcript: [...historyRef.current] });
376
+ } catch (error) {
377
+ optionsRef.current.onError?.(error);
378
+ result = "That request failed before it could finish. Nothing was confirmed.";
379
+ }
380
+ const text = typeof result === "string" ? result.trim() : "";
381
+ if (!text) return;
382
+ for (const content of splitContext(text)) {
383
+ sendEvent({
384
+ type: "session.commentary.append",
385
+ delegation_id: delegationId,
386
+ content
387
+ });
388
+ }
389
+ return;
390
+ }
391
+ const item = nested.item;
392
+ if (type === "response.output_item.done" && item?.type === "function_call") {
393
+ const tool = optionsRef.current.clientTools?.[item.name ?? ""];
394
+ if (!tool || !item.call_id) return;
395
+ let args = {};
396
+ try {
397
+ const parsed = JSON.parse(item.arguments || "{}");
398
+ if (parsed && typeof parsed === "object") args = parsed;
399
+ } catch {
400
+ }
401
+ let output;
402
+ try {
403
+ output = String(await tool(args));
404
+ } catch (error) {
405
+ output = `Tool failed: ${error instanceof Error ? error.message : String(error)}`;
406
+ }
407
+ sendEvent({
408
+ type: "response.item.create",
409
+ item: {
410
+ type: "function_call_output",
411
+ call_id: item.call_id,
412
+ output
413
+ }
414
+ });
415
+ sendEvent({ type: "response.create" });
416
+ }
417
+ }, [flushTranscripts, sendEvent]);
418
+ const end = useCallback2(() => {
419
+ closingRef.current = true;
420
+ flushTranscripts();
421
+ sendEvent({ type: "session.close" });
422
+ dataChannelRef.current?.close();
423
+ pcRef.current?.close();
424
+ mediaStreamRef.current?.getTracks().forEach((track) => track.stop());
425
+ audioRef.current?.pause();
426
+ if (speakingFrameRef.current !== null) cancelAnimationFrame(speakingFrameRef.current);
427
+ speakingFrameRef.current = null;
428
+ pcRef.current = null;
429
+ dataChannelRef.current = null;
430
+ mediaStreamRef.current = null;
431
+ analyserRef.current = null;
432
+ frequencyRef.current = null;
433
+ audioContextRef.current?.close().catch(() => {
434
+ });
435
+ audioContextRef.current = null;
436
+ setIsSpeaking(false);
437
+ setIsListening(false);
438
+ setSessionId(null);
439
+ setStatus("disconnected");
440
+ }, [flushTranscripts, sendEvent]);
441
+ const startInternal = useCallback2(async () => {
442
+ if (status === "connected" || status === "connecting") end();
443
+ closingRef.current = false;
444
+ setStatus("connecting");
445
+ try {
446
+ const stream = await requestMicStream({ audio: true });
447
+ mediaStreamRef.current = stream;
448
+ const pc = new RTCPeerConnection();
449
+ pcRef.current = pc;
450
+ stream.getTracks().forEach((track) => pc.addTrack(track, stream));
451
+ const channel = pc.createDataChannel("oai-events");
452
+ dataChannelRef.current = channel;
453
+ channel.onopen = () => {
454
+ if (!closingRef.current) setIsListening(!mutedRef.current);
455
+ };
456
+ channel.onmessage = (message) => {
457
+ const event = parseEvent(message.data);
458
+ if (event) void handleEvent(event);
459
+ };
460
+ channel.onerror = (event) => optionsRef.current.onError?.(event);
461
+ pc.ontrack = (event) => {
462
+ const remoteStream = event.streams[0];
463
+ if (!remoteStream) return;
464
+ const audio = new Audio();
465
+ audio.autoplay = true;
466
+ audio.srcObject = remoteStream;
467
+ audioRef.current = audio;
468
+ void audio.play().catch(() => {
469
+ });
470
+ try {
471
+ const AudioContextImpl = window.AudioContext || window.webkitAudioContext;
472
+ if (AudioContextImpl) {
473
+ const context = new AudioContextImpl();
474
+ const source = context.createMediaStreamSource(remoteStream);
475
+ const analyser = context.createAnalyser();
476
+ analyser.fftSize = 256;
477
+ source.connect(analyser);
478
+ audioContextRef.current = context;
479
+ analyserRef.current = analyser;
480
+ frequencyRef.current = new Uint8Array(analyser.frequencyBinCount);
481
+ if (speakingFrameRef.current === null) {
482
+ speakingFrameRef.current = requestAnimationFrame(updateSpeaking);
483
+ }
484
+ }
485
+ } catch {
486
+ }
487
+ };
488
+ pc.onconnectionstatechange = () => {
489
+ if (["failed", "disconnected", "closed"].includes(pc.connectionState) && !closingRef.current) {
490
+ optionsRef.current.onError?.(new Error(`GPT-Live connection ${pc.connectionState}`));
491
+ end();
492
+ }
493
+ };
494
+ const offer = await pc.createOffer();
495
+ await pc.setLocalDescription(offer);
496
+ await waitForIceGathering(pc);
497
+ const localSdp = pc.localDescription?.sdp;
498
+ if (!localSdp) throw new Error("Failed to create a WebRTC offer");
499
+ const answer = await mintSession(localSdp);
500
+ await pc.setRemoteDescription({ type: "answer", sdp: answer.sdp });
501
+ setSessionId(answer.sessionId);
502
+ setStatus("connected");
503
+ } catch (error) {
504
+ end();
505
+ optionsRef.current.onError?.(error);
506
+ throw error;
507
+ }
508
+ }, [end, handleEvent, mintSession, status, updateSpeaking]);
509
+ const start = useCallback2(async () => {
510
+ await startInternal();
511
+ }, [startInternal]);
512
+ const restart = useCallback2(async () => {
513
+ end();
514
+ await new Promise((resolve) => window.setTimeout(resolve, 120));
515
+ await startInternal();
516
+ }, [end, startInternal]);
517
+ const setMuted = useCallback2((muted) => {
518
+ mutedRef.current = muted;
519
+ mediaStreamRef.current?.getAudioTracks().forEach((track) => {
520
+ track.enabled = !muted;
521
+ });
522
+ sendEvent({ type: muted ? "session.input_audio.mute" : "session.input_audio.unmute" });
523
+ setIsMuted(muted);
524
+ setIsListening(!muted && status === "connected");
525
+ }, [sendEvent, status]);
526
+ const sendContextualUpdate = useCallback2((text) => {
527
+ for (const content of splitContext(text)) {
528
+ sendEvent({
529
+ type: "session.thinking.append",
530
+ delegation_id: null,
531
+ content
532
+ });
533
+ }
534
+ }, [sendEvent]);
535
+ const sendUserMessage = useCallback2((text) => {
536
+ sendEvent({
537
+ type: "session.commentary.append",
538
+ delegation_id: null,
539
+ content: text
540
+ });
541
+ }, [sendEvent]);
542
+ const sendUserActivity = useCallback2(() => {
543
+ sendEvent({ type: "session.input_audio.commit" });
544
+ }, [sendEvent]);
545
+ const getOutputByteFrequencyData = useCallback2(() => {
546
+ return frequencyRef.current;
547
+ }, []);
548
+ const getTranscript = useCallback2(() => [...historyRef.current], []);
549
+ useEffect2(() => () => end(), [end]);
550
+ return {
551
+ start,
552
+ end,
553
+ restart,
554
+ status,
555
+ isSpeaking,
556
+ isListening,
557
+ isMuted,
558
+ setMuted,
559
+ getOutputByteFrequencyData,
560
+ sendContextualUpdate,
561
+ sendUserMessage,
562
+ sendUserActivity,
563
+ getTranscript,
564
+ conversationId: sessionId,
565
+ convaiConversationId: sessionId
566
+ };
567
+ }
568
+
569
+ // src/react/ShogoVoiceProvider.tsx
570
+ import { ConversationProvider as ElevenLabsConversationProvider } from "@elevenlabs/react";
571
+ import { jsx } from "react/jsx-runtime";
572
+ function ShogoVoiceProvider(props) {
573
+ return /* @__PURE__ */ jsx(ElevenLabsConversationProvider, { children: props.children });
574
+ }
575
+
576
+ export {
577
+ useVoiceConversation,
578
+ useLiveVoiceConversation,
579
+ ShogoVoiceProvider
580
+ };
581
+ //# sourceMappingURL=chunk-ME3SIZZU.js.map
@@ -0,0 +1,17 @@
1
+ // src/shared/chatUrl.ts
2
+ function appendChatQuery(basePath, params) {
3
+ const tail = [];
4
+ for (const [k, v] of Object.entries(params)) {
5
+ if (typeof v === "string" && v.length > 0) {
6
+ tail.push(`${k}=${encodeURIComponent(v)}`);
7
+ }
8
+ }
9
+ if (tail.length === 0) return basePath;
10
+ const sep = basePath.includes("?") ? "&" : "?";
11
+ return `${basePath}${sep}${tail.join("&")}`;
12
+ }
13
+
14
+ export {
15
+ appendChatQuery
16
+ };
17
+ //# sourceMappingURL=chunk-TQOBJMEK.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createVoiceHandlers
3
- } from "./chunk-O362EJQR.js";
3
+ } from "./chunk-CLF25CZC.js";
4
4
 
5
5
  // src/route/index.ts
6
6
  function defaultHandlers() {
@@ -12,6 +12,10 @@ function createVoiceRoute(config) {
12
12
  signedUrl: { GET: (req) => v.signedUrl(req) },
13
13
  ttsPreview: { POST: (req) => v.tts(req) },
14
14
  music: { POST: (req) => v.music(req) },
15
+ op: {
16
+ GET: (req, opId) => v.op(req, opId),
17
+ POST: (req, opId) => v.op(req, opId)
18
+ },
15
19
  agent: {
16
20
  POST: (req) => v.agent.create(req),
17
21
  PATCH: (req) => v.agent.patch(req),
@@ -25,4 +29,4 @@ export {
25
29
  defaultHandlers,
26
30
  createVoiceRoute
27
31
  };
28
- //# sourceMappingURL=chunk-T3T4EBWY.js.map
32
+ //# sourceMappingURL=chunk-UH75UVM2.js.map