@runtypelabs/voice 0.2.4 → 0.4.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/react.cjs CHANGED
@@ -37,13 +37,16 @@ class RuntypePcmPlayerProcessor extends AudioWorkletProcessor {
37
37
  this.readOffset = 0
38
38
  this.buffered = 0
39
39
  this.waiting = true
40
- // INVARIANT: Only report drained after end-of-stream, never during a jitter gap.
40
+ // INVARIANT: Pipeline replies need EOS; continuous sessions report queue drain without a provider turn boundary.
41
41
  this.eosSeen = false
42
+ this.continuous = false
42
43
  this.revision = 0
43
44
  this.port.onmessage = (e) => {
44
45
  const msg = e.data
45
46
  this.revision = msg.revision
46
- if (msg.type === 'push') {
47
+ if (msg.type === 'continuous') {
48
+ this.continuous = msg.enabled
49
+ } else if (msg.type === 'push') {
47
50
  this.eosSeen = false
48
51
  this.chunks.push(msg.samples)
49
52
  this.buffered += msg.samples.length
@@ -83,7 +86,7 @@ class RuntypePcmPlayerProcessor extends AudioWorkletProcessor {
83
86
  }
84
87
  if (this.buffered === 0) {
85
88
  this.waiting = true // mid-reply underrun: re-buffer silently
86
- if (this.eosSeen) {
89
+ if (this.eosSeen || this.continuous) {
87
90
  this.eosSeen = false
88
91
  this.port.postMessage({ type: 'drained', revision: this.revision })
89
92
  }
@@ -127,6 +130,9 @@ async function createPcmPlayer(onDrained) {
127
130
  if (samples.length === 0) return;
128
131
  node.port.postMessage({ type: "push", samples, revision }, [samples.buffer]);
129
132
  },
133
+ setContinuousMode(enabled) {
134
+ node.port.postMessage({ type: "continuous", enabled, revision });
135
+ },
130
136
  endOfStream() {
131
137
  node.port.postMessage({ type: "eos", revision });
132
138
  },
@@ -171,9 +177,58 @@ function initialSnapshot() {
171
177
  error: null,
172
178
  errorDetails: void 0,
173
179
  interruptionMode: "none",
174
- canCancel: false
180
+ canCancel: false,
181
+ artifacts: [],
182
+ session: null
175
183
  };
176
184
  }
185
+ function readString(value) {
186
+ return typeof value === "string" && value ? value : void 0;
187
+ }
188
+ function readArtifactFile(value) {
189
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
190
+ const file = value;
191
+ const path = readString(file.path);
192
+ const mimeType = readString(file.mimeType);
193
+ if (!path || !mimeType) return void 0;
194
+ const language = readString(file.language);
195
+ return { path, mimeType, ...language ? { language } : {} };
196
+ }
197
+ function applyArtifactFrame(existing, frame, identity) {
198
+ if (frame.type === "artifact_start") {
199
+ const title = readString(frame.title);
200
+ const component = readString(frame.component);
201
+ const file = readArtifactFile(frame.file);
202
+ return {
203
+ ...identity,
204
+ artifactType: frame.artifactType === "component" ? "component" : "markdown",
205
+ ...title ? { title } : {},
206
+ ...file ? { file } : {},
207
+ ...component ? { component } : {},
208
+ content: "",
209
+ status: "streaming"
210
+ };
211
+ }
212
+ if (!existing) return void 0;
213
+ if (frame.type === "artifact_delta") {
214
+ const delta = typeof frame.delta === "string" ? frame.delta : "";
215
+ return delta ? { ...existing, content: existing.content + delta } : void 0;
216
+ }
217
+ if (frame.type === "artifact_update") {
218
+ const component = readString(frame.component) ?? existing.component;
219
+ const props = frame.props && typeof frame.props === "object" && !Array.isArray(frame.props) ? frame.props : existing.props;
220
+ return {
221
+ ...existing,
222
+ ...component ? { component } : {},
223
+ ...props ? { props } : {}
224
+ };
225
+ }
226
+ if (frame.type === "artifact_complete") return { ...existing, status: "complete" };
227
+ return void 0;
228
+ }
229
+ function readTurnId(msg) {
230
+ return typeof msg.turnId === "string" && msg.turnId ? msg.turnId : null;
231
+ }
177
232
  var VoiceClient = class {
178
233
  constructor(options) {
179
234
  this.options = options;
@@ -194,6 +249,17 @@ var VoiceClient = class {
194
249
  stoppedResponse = false;
195
250
  responseEnded = true;
196
251
  hasPendingAudio = false;
252
+ /**
253
+ * The assistant entry built from clause captions for the turn being spoken. The
254
+ * authoritative transcript replaces it; a stop commits it, because it captions
255
+ * audio the caller already heard.
256
+ */
257
+ pendingAssistant = null;
258
+ /** Turn whose clause captions were already committed by a stop, so its authoritative text is redundant. */
259
+ committedAssistantTurnId = null;
260
+ activeTurnId = null;
261
+ suppressedArtifactTurnId = null;
262
+ fullDuplex = false;
197
263
  getSnapshot = () => this.snapshot;
198
264
  subscribe = (listener) => {
199
265
  this.listeners.add(listener);
@@ -215,7 +281,7 @@ var VoiceClient = class {
215
281
  if (this.snapshot.status !== "idle" && this.snapshot.status !== "error") return;
216
282
  this.cleanup();
217
283
  const generation = this.generation;
218
- this.update({ ...initialSnapshot(), status: "connecting" });
284
+ this.update({ ...initialSnapshot(), session: this.snapshot.session, status: "connecting" });
219
285
  try {
220
286
  const token = tokenOverride ?? (typeof this.options.clientToken === "function" ? await this.options.clientToken() : this.options.clientToken);
221
287
  if (generation !== this.generation) return;
@@ -230,10 +296,26 @@ var VoiceClient = class {
230
296
  url.pathname = `${basePath}/ws/agents/${encodeURIComponent(this.options.agentId)}/voice`;
231
297
  url.search = "";
232
298
  url.searchParams.set("voiceProtocol", "runtype-browser-v1");
299
+ const capabilities = this.options.artifacts ? "partial_transcript,artifacts" : "partial_transcript";
300
+ url.searchParams.set("clientCapabilities", capabilities);
301
+ const sessionId = this.options.sessionId ?? this.snapshot.session?.sessionId;
302
+ if (sessionId) url.searchParams.set("sessionId", sessionId);
303
+ const visitorToken = typeof this.options.visitorToken === "function" ? await this.options.visitorToken() : this.options.visitorToken;
304
+ if (generation !== this.generation) return;
305
+ if (this.options.visitorToken !== void 0 && !visitorToken) {
306
+ throw new Error(
307
+ "Voice visitor credential unavailable. Initialize the client session first."
308
+ );
309
+ }
310
+ if (visitorToken && !/^cvt_[A-Za-z0-9_-]+$/.test(visitorToken)) {
311
+ throw new Error("Invalid voice visitor credential.");
312
+ }
313
+ if (this.options.fullDuplex !== false)
314
+ url.searchParams.set("voiceCapabilities", "full-duplex-v1");
233
315
  url.hash = "";
234
- const stream = await navigator.mediaDevices.getUserMedia({
316
+ const stream = await (this.options.audioSource?.() ?? navigator.mediaDevices.getUserMedia({
235
317
  audio: { sampleRate: CAPTURE_SAMPLE_RATE, channelCount: 1, echoCancellation: true }
236
- });
318
+ }));
237
319
  if (generation !== this.generation) {
238
320
  stream.getTracks().forEach((track) => track.stop());
239
321
  return;
@@ -253,7 +335,11 @@ var VoiceClient = class {
253
335
  return;
254
336
  }
255
337
  this.player = player;
256
- const socket = new WebSocket(url.toString(), ["runtype.bearer", token]);
338
+ const socket = new WebSocket(url.toString(), [
339
+ "runtype.bearer",
340
+ token,
341
+ ...visitorToken ? [visitorToken] : []
342
+ ]);
257
343
  socket.binaryType = "arraybuffer";
258
344
  this.socket = socket;
259
345
  socket.onopen = () => {
@@ -284,6 +370,11 @@ var VoiceClient = class {
284
370
  this.cleanup();
285
371
  this.update({ status: "idle", interimTranscript: null, audioLevel: 0, isMuted: false });
286
372
  };
373
+ /** Forget the remembered conversation so the next startCall opens a new one. No-op during a call. */
374
+ resetSession = () => {
375
+ if (this.snapshot.status !== "idle" && this.snapshot.status !== "error") return;
376
+ this.update({ session: null });
377
+ };
287
378
  toggleMute = () => {
288
379
  this.update({ isMuted: !this.snapshot.isMuted, audioLevel: 0 });
289
380
  };
@@ -298,9 +389,11 @@ var VoiceClient = class {
298
389
  this.clearPlayback();
299
390
  if (this.snapshot.interruptionMode === "none") {
300
391
  this.stoppedResponse = !this.responseEnded;
392
+ this.commitClauseCaptions();
301
393
  this.setStatus("listening");
302
394
  return;
303
395
  }
396
+ this.suppressedArtifactTurnId = this.activeTurnId;
304
397
  this.awaitingClear = true;
305
398
  this.setStatus("listening");
306
399
  this.socket.send(JSON.stringify({ type: "cancel" }));
@@ -317,6 +410,7 @@ var VoiceClient = class {
317
410
  });
318
411
  }
319
412
  cleanup() {
413
+ this.fullDuplex = false;
320
414
  this.generation += 1;
321
415
  if (this.processor) this.processor.onaudioprocess = null;
322
416
  this.processor?.disconnect();
@@ -342,6 +436,82 @@ var VoiceClient = class {
342
436
  this.awaitingClear = false;
343
437
  this.stoppedResponse = false;
344
438
  this.responseEnded = true;
439
+ this.commitClauseCaptions();
440
+ this.committedAssistantTurnId = null;
441
+ this.activeTurnId = null;
442
+ this.suppressedArtifactTurnId = null;
443
+ const settledArtifacts = this.snapshot.artifacts.filter(
444
+ (artifact) => artifact.status === "complete"
445
+ );
446
+ if (settledArtifacts.length !== this.snapshot.artifacts.length)
447
+ this.update({ artifacts: settledArtifacts });
448
+ }
449
+ /**
450
+ * Folds an `artifact` message into the snapshot. Frames of a turn the caller
451
+ * cancelled are dropped; a playback-only stop leaves the turn running, so its
452
+ * artifacts keep arriving and are kept.
453
+ */
454
+ applyArtifactMessage(msg) {
455
+ if (this.awaitingClear) return;
456
+ const frame = msg.event;
457
+ if (!frame || typeof frame !== "object" || Array.isArray(frame)) return;
458
+ const event = frame;
459
+ const id = readString(event.id);
460
+ if (!id) return;
461
+ const turnId = readTurnId(msg);
462
+ if (turnId !== null && turnId === this.suppressedArtifactTurnId) return;
463
+ if (turnId !== null) this.activeTurnId = turnId;
464
+ const artifacts = this.snapshot.artifacts;
465
+ const index = artifacts.findIndex((artifact) => artifact.id === id);
466
+ const executionId = readString(msg.executionId);
467
+ const next = applyArtifactFrame(index === -1 ? void 0 : artifacts[index], event, {
468
+ id,
469
+ turnId,
470
+ ...executionId ? { executionId } : {}
471
+ });
472
+ if (!next) return;
473
+ this.update({
474
+ artifacts: index === -1 ? [...artifacts, next] : [...artifacts.slice(0, index), next, ...artifacts.slice(index + 1)]
475
+ });
476
+ }
477
+ /** Settles the spoken clauses in place and ends the turn's claim on them. */
478
+ commitClauseCaptions() {
479
+ this.committedAssistantTurnId = this.pendingAssistant?.turnId ?? null;
480
+ const settled = this.pendingAssistant !== null;
481
+ this.pendingAssistant = null;
482
+ if (!settled) return;
483
+ const transcript = this.snapshot.transcript;
484
+ const last = transcript[transcript.length - 1];
485
+ if (last?.role !== "assistant" || last.partial !== true) return;
486
+ const { partial: _partial, ...committed } = last;
487
+ this.update({ transcript: [...transcript.slice(0, -1), committed] });
488
+ }
489
+ /** Extends the turn's spoken-so-far assistant entry, or opens one for a new turn. */
490
+ appendClauseCaption(text, turnId) {
491
+ const transcript = this.snapshot.transcript;
492
+ const last = transcript[transcript.length - 1];
493
+ if (this.pendingAssistant?.turnId === turnId && last?.role === "assistant") {
494
+ return {
495
+ interimTranscript: null,
496
+ transcript: [...transcript.slice(0, -1), { ...last, content: last.content + text }],
497
+ status: "speaking"
498
+ };
499
+ }
500
+ this.pendingAssistant = { turnId };
501
+ return {
502
+ interimTranscript: null,
503
+ transcript: [
504
+ ...transcript,
505
+ {
506
+ role: "assistant",
507
+ content: text,
508
+ timestamp: Date.now(),
509
+ partial: true,
510
+ ...turnId ? { turnId } : {}
511
+ }
512
+ ],
513
+ status: "speaking"
514
+ };
345
515
  }
346
516
  clearPlayback() {
347
517
  this.playbackRevision += 1;
@@ -376,32 +546,89 @@ var VoiceClient = class {
376
546
  return;
377
547
  }
378
548
  switch (msg.type) {
379
- case "session_config":
549
+ case "session_config": {
550
+ this.fullDuplex = msg.speechMode === "speech_to_speech";
551
+ if (this.fullDuplex) player.setContinuousMode?.(true);
380
552
  if (msg.interruptionMode === "none" || msg.interruptionMode === "cancel" || msg.interruptionMode === "barge-in") {
381
553
  this.update({ interruptionMode: msg.interruptionMode });
382
554
  }
555
+ const sessionId = readString(msg.sessionId);
556
+ const conversationId = readString(msg.conversationId);
557
+ if (sessionId && conversationId) {
558
+ const session = { sessionId, conversationId };
559
+ this.update({ session });
560
+ this.options.onSession?.(session);
561
+ }
562
+ break;
563
+ }
564
+ case "artifact":
565
+ this.applyArtifactMessage(msg);
566
+ break;
567
+ case "transcript_update": {
568
+ if (typeof msg.text !== "string" || typeof msg.turnId !== "string" || msg.role !== "user" && msg.role !== "assistant")
569
+ break;
570
+ if (msg.role === "assistant" && this.awaitingClear) break;
571
+ const entries = [...this.snapshot.transcript];
572
+ const index = entries.findIndex(
573
+ (entry2) => entry2.turnId === msg.turnId && entry2.role === msg.role
574
+ );
575
+ const entry = {
576
+ role: msg.role,
577
+ content: msg.text,
578
+ turnId: msg.turnId,
579
+ isFinal: msg.final === true,
580
+ timestamp: index >= 0 ? entries[index].timestamp : Date.now()
581
+ };
582
+ if (index >= 0) entries[index] = entry;
583
+ else entries.push(entry);
584
+ this.update({ transcript: entries, interimTranscript: null });
585
+ break;
586
+ }
587
+ case "delegation_started":
588
+ if (!this.hasPendingAudio && !this.awaitingClear) this.setStatus("thinking");
589
+ break;
590
+ case "delegation_completed":
591
+ if (!this.hasPendingAudio && !this.awaitingClear) this.setStatus("listening");
383
592
  break;
384
593
  case "transcript_interim":
385
594
  this.update({ interimTranscript: typeof msg.text === "string" ? msg.text || null : null });
386
595
  break;
387
- case "transcript_final":
596
+ case "transcript_partial": {
597
+ if (msg.role !== "assistant" || typeof msg.text !== "string" || !msg.text) break;
598
+ this.activeTurnId = readTurnId(msg) ?? this.activeTurnId;
599
+ if (this.awaitingClear || this.stoppedResponse) break;
600
+ this.responseEnded = false;
601
+ this.update(this.appendClauseCaption(msg.text, readTurnId(msg)));
602
+ break;
603
+ }
604
+ case "transcript_final": {
388
605
  if (typeof msg.text !== "string" || msg.role !== "user" && msg.role !== "assistant") break;
389
- if (msg.role === "assistant" && (this.awaitingClear || this.stoppedResponse)) break;
606
+ const role = msg.role;
607
+ const text = msg.text;
608
+ if (role === "assistant" && (this.awaitingClear || this.stoppedResponse)) break;
609
+ const turnId = readTurnId(msg);
610
+ if (turnId !== null) this.activeTurnId = turnId;
611
+ if (role === "assistant" && turnId !== null && turnId === this.committedAssistantTurnId)
612
+ break;
390
613
  this.responseEnded = false;
614
+ const transcript = this.snapshot.transcript;
615
+ const last = transcript[transcript.length - 1];
616
+ const supersedes = role === "assistant" && this.pendingAssistant !== null && last?.role === "assistant" && (turnId === null || this.pendingAssistant.turnId === null || this.pendingAssistant.turnId === turnId);
617
+ this.pendingAssistant = null;
618
+ const entry = {
619
+ role,
620
+ content: text,
621
+ // INVARIANT: reuse the caption's timestamp so a keyed list does not remount the bubble.
622
+ timestamp: supersedes && last ? last.timestamp : Date.now(),
623
+ ...turnId ? { turnId } : {}
624
+ };
391
625
  this.update({
392
626
  interimTranscript: null,
393
- transcript: [
394
- ...this.snapshot.transcript,
395
- {
396
- role: msg.role,
397
- content: msg.text,
398
- timestamp: Date.now(),
399
- ...typeof msg.turnId === "string" && msg.turnId ? { turnId: msg.turnId } : {}
400
- }
401
- ],
402
- status: this.awaitingClear ? this.snapshot.status : msg.role === "user" ? "thinking" : "speaking"
627
+ transcript: supersedes ? [...transcript.slice(0, -1), entry] : [...transcript, entry],
628
+ status: this.awaitingClear ? this.snapshot.status : role === "user" ? "thinking" : "speaking"
403
629
  });
404
630
  break;
631
+ }
405
632
  case "audio_end": {
406
633
  this.responseEnded = true;
407
634
  if (this.stoppedResponse) {
@@ -418,10 +645,12 @@ var VoiceClient = class {
418
645
  break;
419
646
  }
420
647
  case "audio_clear":
648
+ this.suppressedArtifactTurnId = this.activeTurnId ?? this.suppressedArtifactTurnId;
421
649
  this.clearPlayback();
422
650
  this.awaitingClear = false;
423
651
  this.stoppedResponse = false;
424
652
  this.responseEnded = true;
653
+ this.commitClauseCaptions();
425
654
  this.setStatus("listening");
426
655
  break;
427
656
  case "metrics": {
@@ -434,8 +663,11 @@ var VoiceClient = class {
434
663
  // @snake-case-ok: Existing voice wire contract.
435
664
  firstAudioMs: number(msg.first_audio_ms),
436
665
  // @snake-case-ok: Existing voice wire contract.
437
- totalMs: number(msg.total_ms)
666
+ totalMs: number(msg.total_ms),
667
+ // @snake-case-ok: Existing voice wire contract.
668
+ firstSynthesisMs: number(msg.first_synthesis_ms),
438
669
  // @snake-case-ok: Existing voice wire contract.
670
+ incremental: msg.incremental === true
439
671
  }
440
672
  });
441
673
  break;
@@ -467,14 +699,14 @@ var VoiceClient = class {
467
699
  const processor = context.createScriptProcessor(CAPTURE_BUFFER_SIZE, 1, 1);
468
700
  this.processor = processor;
469
701
  processor.onaudioprocess = (event) => {
470
- if (generation !== this.generation || this.snapshot.isMuted) return;
702
+ if (generation !== this.generation || this.snapshot.isMuted && !this.fullDuplex) return;
471
703
  const input = event.inputBuffer.getChannelData(0);
472
704
  let sum = 0;
473
705
  for (const sample of input) sum += sample * sample;
474
- this.update({ audioLevel: Math.sqrt(sum / input.length) });
706
+ this.update({ audioLevel: this.snapshot.isMuted ? 0 : Math.sqrt(sum / input.length) });
475
707
  if (socket.readyState !== WebSocket.OPEN) return;
476
708
  const pcm = new Int16Array(input.length);
477
- if (this.snapshot.interruptionMode === "cancel" && (this.awaitingClear || this.snapshot.status === "thinking" || this.hasPendingAudio || this.snapshot.status === "speaking")) {
709
+ if (this.snapshot.isMuted || this.snapshot.interruptionMode === "cancel" && (this.awaitingClear || this.snapshot.status === "thinking" || this.hasPendingAudio || this.snapshot.status === "speaking")) {
478
710
  socket.send(pcm.buffer);
479
711
  return;
480
712
  }
@@ -490,25 +722,81 @@ var VoiceClient = class {
490
722
  };
491
723
 
492
724
  // src/react.ts
493
- function useVoiceClient({ agentId, apiUrl, clientToken }) {
725
+ function useVoiceClient({
726
+ agentId,
727
+ apiUrl,
728
+ clientToken,
729
+ artifacts,
730
+ sessionId,
731
+ visitorToken,
732
+ onSession,
733
+ fullDuplex,
734
+ audioSource
735
+ }) {
736
+ const sessionRef = (0, import_react.useRef)(sessionId);
737
+ (0, import_react.useEffect)(() => {
738
+ sessionRef.current = sessionId;
739
+ }, [sessionId]);
494
740
  const tokenRef = (0, import_react.useRef)(clientToken);
741
+ const visitorRef = (0, import_react.useRef)(visitorToken);
742
+ const sessionCallbackRef = (0, import_react.useRef)(onSession);
743
+ const audioSourceRef = (0, import_react.useRef)(audioSource);
744
+ const hasAudioSource = audioSource !== void 0;
745
+ (0, import_react.useEffect)(() => {
746
+ visitorRef.current = visitorToken;
747
+ sessionCallbackRef.current = onSession;
748
+ }, [visitorToken, onSession]);
749
+ (0, import_react.useEffect)(() => {
750
+ audioSourceRef.current = audioSource;
751
+ }, [audioSource]);
495
752
  (0, import_react.useEffect)(() => {
496
753
  tokenRef.current = clientToken;
497
754
  }, [clientToken]);
498
- const client = (0, import_react.useMemo)(
499
- () => new VoiceClient({
755
+ const createClient = () => new VoiceClient({
756
+ agentId,
757
+ apiUrl,
758
+ artifacts,
759
+ fullDuplex,
760
+ ...hasAudioSource ? { audioSource: () => audioSourceRef.current() } : {},
761
+ get sessionId() {
762
+ return sessionRef.current;
763
+ },
764
+ get visitorToken() {
765
+ return visitorRef.current;
766
+ },
767
+ onSession: (session) => sessionCallbackRef.current?.(session),
768
+ clientToken: () => typeof tokenRef.current === "function" ? tokenRef.current() : tokenRef.current
769
+ });
770
+ const [binding, setBinding] = (0, import_react.useState)(() => ({
771
+ agentId,
772
+ apiUrl,
773
+ artifacts,
774
+ fullDuplex,
775
+ hasAudioSource,
776
+ sessionId,
777
+ client: createClient()
778
+ }));
779
+ const configurationChanged = binding.agentId !== agentId || binding.apiUrl !== apiUrl || binding.artifacts !== artifacts || binding.fullDuplex !== fullDuplex || binding.hasAudioSource !== hasAudioSource;
780
+ if (configurationChanged || binding.sessionId !== sessionId) {
781
+ const acknowledgesSession = sessionId !== void 0 && sessionId === binding.client.getSnapshot().session?.sessionId;
782
+ setBinding({
500
783
  agentId,
501
784
  apiUrl,
502
- clientToken: () => typeof tokenRef.current === "function" ? tokenRef.current() : tokenRef.current
503
- }),
504
- [agentId, apiUrl]
505
- );
785
+ artifacts,
786
+ fullDuplex,
787
+ hasAudioSource,
788
+ sessionId,
789
+ client: !configurationChanged && acknowledgesSession ? binding.client : createClient()
790
+ });
791
+ }
792
+ const client = binding.client;
506
793
  const snapshot = (0, import_react.useSyncExternalStore)(client.subscribe, client.getSnapshot, client.getSnapshot);
507
794
  (0, import_react.useEffect)(() => () => client.endCall(), [client]);
508
795
  return {
509
796
  ...snapshot,
510
797
  startCall: client.startCall,
511
798
  endCall: client.endCall,
799
+ resetSession: client.resetSession,
512
800
  toggleMute: client.toggleMute,
513
801
  cancelResponse: client.cancelResponse
514
802
  };
package/dist/react.d.cts CHANGED
@@ -1,10 +1,11 @@
1
- import { V as VoiceClientOptions, c as VoiceStatus, T as TranscriptEntry, b as VoiceMetrics, I as InterruptionMode } from './types-9SfA7oHc.cjs';
2
- export { a as VoiceSnapshot } from './types-9SfA7oHc.cjs';
1
+ import { V as VoiceClientOptions, f as VoiceStatus, T as TranscriptEntry, d as VoiceMetrics, I as InterruptionMode, b as VoiceArtifact, e as VoiceSession } from './types-Dkh4LxEA.cjs';
2
+ export { a as VoiceSnapshot } from './types-Dkh4LxEA.cjs';
3
3
 
4
4
  /** Subscribe to a voice client and end its call when the component unmounts or changes agent. */
5
- declare function useVoiceClient({ agentId, apiUrl, clientToken }: VoiceClientOptions): {
5
+ declare function useVoiceClient({ agentId, apiUrl, clientToken, artifacts, sessionId, visitorToken, onSession, fullDuplex, audioSource, }: VoiceClientOptions): {
6
6
  startCall: (tokenOverride?: string) => Promise<void>;
7
7
  endCall: () => void;
8
+ resetSession: () => void;
8
9
  toggleMute: () => void;
9
10
  cancelResponse: () => void;
10
11
  status: VoiceStatus;
@@ -22,6 +23,8 @@ declare function useVoiceClient({ agentId, apiUrl, clientToken }: VoiceClientOpt
22
23
  };
23
24
  interruptionMode: InterruptionMode;
24
25
  canCancel: boolean;
26
+ artifacts: readonly VoiceArtifact[];
27
+ session: VoiceSession | null;
25
28
  };
26
29
 
27
- export { VoiceClientOptions, VoiceStatus, useVoiceClient };
30
+ export { VoiceArtifact, VoiceClientOptions, VoiceSession, VoiceStatus, useVoiceClient };
package/dist/react.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { V as VoiceClientOptions, c as VoiceStatus, T as TranscriptEntry, b as VoiceMetrics, I as InterruptionMode } from './types-9SfA7oHc.js';
2
- export { a as VoiceSnapshot } from './types-9SfA7oHc.js';
1
+ import { V as VoiceClientOptions, f as VoiceStatus, T as TranscriptEntry, d as VoiceMetrics, I as InterruptionMode, b as VoiceArtifact, e as VoiceSession } from './types-Dkh4LxEA.js';
2
+ export { a as VoiceSnapshot } from './types-Dkh4LxEA.js';
3
3
 
4
4
  /** Subscribe to a voice client and end its call when the component unmounts or changes agent. */
5
- declare function useVoiceClient({ agentId, apiUrl, clientToken }: VoiceClientOptions): {
5
+ declare function useVoiceClient({ agentId, apiUrl, clientToken, artifacts, sessionId, visitorToken, onSession, fullDuplex, audioSource, }: VoiceClientOptions): {
6
6
  startCall: (tokenOverride?: string) => Promise<void>;
7
7
  endCall: () => void;
8
+ resetSession: () => void;
8
9
  toggleMute: () => void;
9
10
  cancelResponse: () => void;
10
11
  status: VoiceStatus;
@@ -22,6 +23,8 @@ declare function useVoiceClient({ agentId, apiUrl, clientToken }: VoiceClientOpt
22
23
  };
23
24
  interruptionMode: InterruptionMode;
24
25
  canCancel: boolean;
26
+ artifacts: readonly VoiceArtifact[];
27
+ session: VoiceSession | null;
25
28
  };
26
29
 
27
- export { VoiceClientOptions, VoiceStatus, useVoiceClient };
30
+ export { VoiceArtifact, VoiceClientOptions, VoiceSession, VoiceStatus, useVoiceClient };