@convai/web-sdk 1.5.0 → 1.6.0-beta.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 (43) hide show
  1. package/README.md +89 -36
  2. package/dist/core/BlendshapeQueue.d.ts +75 -4
  3. package/dist/core/BlendshapeQueue.d.ts.map +1 -1
  4. package/dist/core/BlendshapeQueue.js +400 -24
  5. package/dist/core/BlendshapeQueue.js.map +1 -1
  6. package/dist/core/ConvaiClient.d.ts +10 -2
  7. package/dist/core/ConvaiClient.d.ts.map +1 -1
  8. package/dist/core/ConvaiClient.js +208 -57
  9. package/dist/core/ConvaiClient.js.map +1 -1
  10. package/dist/core/MessageHandler.d.ts +8 -0
  11. package/dist/core/MessageHandler.d.ts.map +1 -1
  12. package/dist/core/MessageHandler.js +63 -12
  13. package/dist/core/MessageHandler.js.map +1 -1
  14. package/dist/core/VideoManager.d.ts +40 -5
  15. package/dist/core/VideoManager.d.ts.map +1 -1
  16. package/dist/core/VideoManager.js +579 -30
  17. package/dist/core/VideoManager.js.map +1 -1
  18. package/dist/core/connectRequest.d.ts +23 -0
  19. package/dist/core/connectRequest.d.ts.map +1 -0
  20. package/dist/core/connectRequest.js +31 -0
  21. package/dist/core/connectRequest.js.map +1 -0
  22. package/dist/core/contextUpdateRequest.d.ts +4 -0
  23. package/dist/core/contextUpdateRequest.d.ts.map +1 -0
  24. package/dist/core/contextUpdateRequest.js +10 -0
  25. package/dist/core/contextUpdateRequest.js.map +1 -0
  26. package/dist/core/index.d.ts +1 -1
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/types.d.ts +172 -3
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/core/videoSourceState.d.ts +4 -0
  31. package/dist/core/videoSourceState.d.ts.map +1 -0
  32. package/dist/core/videoSourceState.js +9 -0
  33. package/dist/core/videoSourceState.js.map +1 -0
  34. package/dist/core/visionRequest.d.ts +4 -0
  35. package/dist/core/visionRequest.d.ts.map +1 -0
  36. package/dist/core/visionRequest.js +42 -0
  37. package/dist/core/visionRequest.js.map +1 -0
  38. package/dist/react/hooks/useConvaiClient.d.ts.map +1 -1
  39. package/dist/react/hooks/useConvaiClient.js +3 -0
  40. package/dist/react/hooks/useConvaiClient.js.map +1 -1
  41. package/dist/vanilla/types.d.ts +6 -0
  42. package/dist/vanilla/types.d.ts.map +1 -1
  43. package/package.json +5 -2
@@ -1,5 +1,5 @@
1
1
  import { Room } from "livekit-client";
2
- import { ConvaiConfig, ConvaiClientState, ChatMessage, IConvaiClient, AudioControls, VideoControls, ScreenShareControls, DynamicInfo, ContextUpdateOptions, UploadFileOptions, WebSocketSessionFactory } from "./types";
2
+ import { ConvaiConfig, ConvaiClientState, ChatMessage, IConvaiClient, AudioControls, VideoControls, ScreenShareControls, DynamicInfo, ContextUpdateOptions, UploadFileOptions, WebSocketSessionFactory, RespondModeUpdateOptions, UpdateSceneMetadataOptions, VisionStatusOptions, VisionTriggerOptions } from "./types";
3
3
  import { EventEmitter } from "./EventEmitter";
4
4
  import { BlendshapeQueue } from "./BlendshapeQueue";
5
5
  import { MemoryManager } from "./MemoryManager";
@@ -57,6 +57,7 @@ export declare class ConvaiClient extends EventEmitter implements IConvaiClient
57
57
  private _wsSession;
58
58
  private _activeTransport;
59
59
  private _logRtviMessages;
60
+ private _isDisconnecting;
60
61
  private _clientReadyRetryTimer;
61
62
  private _clientReadyTimeoutTimer;
62
63
  private _clientReadyMsgId;
@@ -122,6 +123,7 @@ export declare class ConvaiClient extends EventEmitter implements IConvaiClient
122
123
  * Connect to a Convai character
123
124
  */
124
125
  connect(config?: ConvaiConfig): Promise<void>;
126
+ private cleanupWebSocketSessionAfterConnectFailure;
125
127
  /**
126
128
  * RTVI client-ready handshake. Server-side gates bot-ready emission on
127
129
  * receipt of client-ready (and re-emits bot-ready on every client-ready),
@@ -160,6 +162,7 @@ export declare class ConvaiClient extends EventEmitter implements IConvaiClient
160
162
  * is clean before a new response starts arriving.
161
163
  */
162
164
  private endCurrentConversationIfActive;
165
+ private startTrackedConversation;
163
166
  /**
164
167
  * Send an interrupt message to stop the bot's current response
165
168
  */
@@ -201,6 +204,9 @@ export declare class ConvaiClient extends EventEmitter implements IConvaiClient
201
204
  * ```
202
205
  */
203
206
  updateContext(options: ContextUpdateOptions): void;
207
+ visionStatus(options?: VisionStatusOptions): string | null;
208
+ visionTrigger(options?: VisionTriggerOptions): string | null;
209
+ respondModeUpdate(options: RespondModeUpdateOptions): string | null;
204
210
  /**
205
211
  * Send descriptive scene updates. Use when environment description changes
206
212
  * and the bot should know what is visible or nearby. Does not modify action_config.
@@ -208,7 +214,9 @@ export declare class ConvaiClient extends EventEmitter implements IConvaiClient
208
214
  updateSceneMetadata(items: Array<{
209
215
  name: string;
210
216
  description: string;
211
- }>): void;
217
+ }>, options?: UpdateSceneMetadataOptions): void;
218
+ private createUpdateId;
219
+ private serializeRespondModes;
212
220
  /**
213
221
  * Upload a file to the character via the LiveKit byte stream transport.
214
222
  * Only works when connected with the LiveKit transport.
@@ -1 +1 @@
1
- {"version":3,"file":"ConvaiClient.d.ts","sourceRoot":"","sources":["../../src/core/ConvaiClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ConvaiClient.d.ts","sourceRoot":"","sources":["../../src/core/ConvaiClient.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { Room, RoomEvent, DefaultReconnectPolicy, setLogLevel, } from "livekit-client";
2
2
  setLogLevel("warn");
3
+ import { DisconnectReasonEnum, } from "./types";
3
4
  import { AudioManager } from "./AudioManager";
4
5
  import { VideoManager } from "./VideoManager";
5
6
  import { ScreenShareManager } from "./ScreenShareManager";
@@ -7,6 +8,9 @@ import { MessageHandler } from "./MessageHandler";
7
8
  import { EventEmitter } from "./EventEmitter";
8
9
  import { MemoryManager } from "./MemoryManager";
9
10
  import { ConnectionStateHandler } from "./ConnectionStateHandler";
11
+ import { buildBlendshapeConnectConfig } from "./connectRequest";
12
+ import { shouldPreemptForContextUpdateResponse, shouldPreemptForExplicitRespondMode, } from "./contextUpdateRequest";
13
+ import { resolveVisionInputConfig, serializeVisionInputConfig, } from "./visionRequest";
10
14
  // Module-level registry — populated by importing @convai/web-sdk/vanilla/websocket
11
15
  let _wsFactory = null;
12
16
  /**
@@ -78,6 +82,7 @@ export class ConvaiClient extends EventEmitter {
78
82
  this._wsSession = null;
79
83
  this._activeTransport = null;
80
84
  this._logRtviMessages = true;
85
+ this._isDisconnecting = false;
81
86
  // RTVI client-ready handshake (race-proof bot-ready delivery)
82
87
  this._clientReadyRetryTimer = null;
83
88
  this._clientReadyTimeoutTimer = null;
@@ -244,8 +249,9 @@ export class ConvaiClient extends EventEmitter {
244
249
  if (!wasListening && isListening) {
245
250
  // Reset blendshape queue if mid-conversation, but don't interrupt the server
246
251
  if (this.blendshapeQueue.isConversationActive() ||
247
- this._state.isSpeaking) {
248
- this.blendshapeQueue.interrupt();
252
+ this._state.isSpeaking ||
253
+ this.blendshapeQueue.hasBufferedFrames()) {
254
+ this._messageHandler.interruptLipsyncPlayback();
249
255
  }
250
256
  this._conversationSessionId++;
251
257
  this._conversationStartTime = Date.now();
@@ -277,6 +283,10 @@ export class ConvaiClient extends EventEmitter {
277
283
  this._messageHandler.on("blendshapeStatsReceived", (stats) => {
278
284
  this.emit("blendshapeStatsReceived", stats);
279
285
  });
286
+ // Forward blendshape cancel event (owner-scoped visual hard stop)
287
+ this._messageHandler.on("blendshapeCancel", (data) => {
288
+ this.emit("blendshapeCancel", data);
289
+ });
280
290
  // Forward idle warning event
281
291
  this._messageHandler.on("idleWarning", (data) => {
282
292
  this.emit("idleWarning", data);
@@ -419,6 +429,7 @@ export class ConvaiClient extends EventEmitter {
419
429
  this._connectionType = connType;
420
430
  const transportType = configWithDefaults.transport ?? "livekit";
421
431
  this._activeTransport = transportType;
432
+ const effectiveVisionInputConfig = resolveVisionInputConfig(configWithDefaults);
422
433
  // Prepare request body with required parameters
423
434
  const characterSessionIdToSend = configWithDefaults.characterSessionId ?? this._characterSessionId;
424
435
  const requestBody = {
@@ -431,16 +442,7 @@ export class ConvaiClient extends EventEmitter {
431
442
  }),
432
443
  transport: transportType,
433
444
  connection_type: connType,
434
- blendshape_provider: configWithDefaults.enableLipsync
435
- ? "neurosync"
436
- : "not_provided",
437
- blendshape_config: {
438
- enable_chunking: true,
439
- chunk_size: 10,
440
- format: configWithDefaults.blendshapeConfig?.format || "mha",
441
- frames_buffer_duration: configWithDefaults.blendshapeConfig?.frames_buffer_duration ?? 0.1,
442
- output_fps: 90,
443
- },
445
+ ...buildBlendshapeConnectConfig(configWithDefaults),
444
446
  llm_provider: "dynamic",
445
447
  ...(configWithDefaults.enableEmotion === true && {
446
448
  emotion_config: (() => {
@@ -461,6 +463,12 @@ export class ConvaiClient extends EventEmitter {
461
463
  ? configWithDefaults.ttsEnabled
462
464
  : true,
463
465
  default_stt_enabled: true,
466
+ ...(effectiveVisionInputConfig && {
467
+ vision_input_config: serializeVisionInputConfig(effectiveVisionInputConfig),
468
+ }),
469
+ ...(configWithDefaults.respondModes && {
470
+ respond_modes: this.serializeRespondModes(configWithDefaults.respondModes),
471
+ }),
464
472
  ...(characterSessionIdToSend &&
465
473
  characterSessionIdToSend !== "-1" && {
466
474
  character_session_id: characterSessionIdToSend,
@@ -489,7 +497,7 @@ export class ConvaiClient extends EventEmitter {
489
497
  invocation_metadata: {
490
498
  source: configWithDefaults.invocationMetadata?.source ?? "web_sdk",
491
499
  client_version: configWithDefaults.invocationMetadata?.clientVersion ??
492
- "1.3.2-beta.0",
500
+ "1.6.0-beta.0",
493
501
  ...(configWithDefaults.invocationMetadata?.extraMetadata && {
494
502
  extra_metadata: configWithDefaults.invocationMetadata.extraMetadata,
495
503
  }),
@@ -569,32 +577,69 @@ export class ConvaiClient extends EventEmitter {
569
577
  // after the connection is established (enableMic(false) works once the
570
578
  // stream exists, but not before it is initialized).
571
579
  this._wsSession = _wsFactory((payload) => this._messageHandler.handleDataReceivedPublic(payload), true);
572
- this._wsSession.on("botAudioTrack", (track) => {
580
+ const webSocketVisionEnabled = configWithDefaults.enableVideo === true &&
581
+ effectiveVisionInputConfig?.enabled !== false;
582
+ if (webSocketVisionEnabled) {
583
+ this._videoManager.setWebSocketVisionMessageSender((type, data) => {
584
+ if (this._activeTransport !== "websocket" ||
585
+ !this._wsSession?.isConnected) {
586
+ return;
587
+ }
588
+ this._wsSession.sendMessage(type, data);
589
+ });
590
+ }
591
+ else {
592
+ this._videoManager.setWebSocketVisionDisabled();
593
+ }
594
+ const wsSession = this._wsSession;
595
+ wsSession.on("botAudioTrack", (track) => {
573
596
  this.emit("botAudioTrack", track);
574
597
  });
575
- this._wsSession.on("disconnected", () => {
598
+ wsSession.on("disconnected", () => {
599
+ if (this._wsSession !== wsSession || this._isDisconnecting) {
600
+ return;
601
+ }
576
602
  this._connectionStateHandler.handleDisconnected();
603
+ this.clearAllManagers();
577
604
  });
578
605
  this._audioManager.setWebSocketSession(this._wsSession);
579
606
  // Wait for WebSocket transport to connect (fires before bot-ready)
580
607
  await new Promise((resolve, reject) => {
608
+ let settled = false;
609
+ const cleanupConnectListeners = () => {
610
+ wsSession.off("connected", onConnected);
611
+ wsSession.off("error", onError);
612
+ wsSession.off("disconnected", onDisconnected);
613
+ };
614
+ const settle = (callback) => {
615
+ if (settled) {
616
+ return;
617
+ }
618
+ settled = true;
619
+ cleanupConnectListeners();
620
+ callback();
621
+ };
581
622
  const onConnected = () => {
582
- this._wsSession.off("connected", onConnected);
583
- this._wsSession.off("error", onError);
584
- resolve();
623
+ settle(resolve);
585
624
  };
586
625
  const onError = (err) => {
587
- this._wsSession.off("connected", onConnected);
588
- this._wsSession.off("error", onError);
589
- reject(err instanceof Error ? err : new Error(String(err)));
626
+ settle(() => {
627
+ reject(err instanceof Error ? err : new Error(String(err)));
628
+ });
629
+ };
630
+ const onDisconnected = () => {
631
+ settle(() => {
632
+ reject(new Error("WebSocket disconnected before connect completed"));
633
+ });
590
634
  };
591
- this._wsSession.on("connected", onConnected);
592
- this._wsSession.on("error", onError);
635
+ wsSession.on("connected", onConnected);
636
+ wsSession.on("error", onError);
637
+ wsSession.on("disconnected", onDisconnected);
593
638
  // connectWithUrl calls initDevices() then connect({ wsUrl }) — matches sandbox flow
594
- this._wsSession.connectWithUrl(wsUrl).catch((err) => {
595
- this._wsSession.off("connected", onConnected);
596
- this._wsSession.off("error", onError);
597
- reject(err instanceof Error ? err : new Error(String(err)));
639
+ wsSession.connectWithUrl(wsUrl).catch((err) => {
640
+ settle(() => {
641
+ reject(err instanceof Error ? err : new Error(String(err)));
642
+ });
598
643
  });
599
644
  });
600
645
  // For WebSocket, mic is already streaming after connect.
@@ -608,8 +653,14 @@ export class ConvaiClient extends EventEmitter {
608
653
  else {
609
654
  this._audioManager.syncWsAudioState(true);
610
655
  }
656
+ if (webSocketVisionEnabled &&
657
+ configWithDefaults.enableVideo &&
658
+ configWithDefaults.startWithVideoOn) {
659
+ await this._videoManager.enableVideo();
660
+ }
611
661
  }
612
662
  else {
663
+ this._videoManager.setWebSocketVisionMessageSender(null);
613
664
  // ── LiveKit transport path (default) ─────────────────────────────
614
665
  await this._room.connect(connectionData.room_url, connectionData.token, {
615
666
  rtcConfig: {
@@ -659,6 +710,7 @@ export class ConvaiClient extends EventEmitter {
659
710
  this._startClientReadyHandshake();
660
711
  }
661
712
  catch (error) {
713
+ await this.cleanupWebSocketSessionAfterConnectFailure();
662
714
  this.updateState({
663
715
  isConnected: false,
664
716
  isConnecting: false,
@@ -667,6 +719,22 @@ export class ConvaiClient extends EventEmitter {
667
719
  throw error;
668
720
  }
669
721
  }
722
+ async cleanupWebSocketSessionAfterConnectFailure() {
723
+ if (this._activeTransport !== "websocket" || !this._wsSession) {
724
+ return;
725
+ }
726
+ this._isDisconnecting = true;
727
+ try {
728
+ await this._wsSession.disconnect();
729
+ }
730
+ catch {
731
+ // ignore cleanup errors while preserving the original connect failure
732
+ }
733
+ finally {
734
+ this._isDisconnecting = false;
735
+ }
736
+ this.clearAllManagers();
737
+ }
670
738
  /**
671
739
  * RTVI client-ready handshake. Server-side gates bot-ready emission on
672
740
  * receipt of client-ready (and re-emits bot-ready on every client-ready),
@@ -732,14 +800,18 @@ export class ConvaiClient extends EventEmitter {
732
800
  async disconnect() {
733
801
  this._stopClientReadyHandshake();
734
802
  if (this._activeTransport === "websocket" && this._wsSession) {
803
+ this._isDisconnecting = true;
735
804
  try {
736
805
  await this._wsSession.disconnect();
737
806
  }
738
807
  catch {
739
808
  // ignore disconnect errors
740
809
  }
810
+ finally {
811
+ this._isDisconnecting = false;
812
+ }
741
813
  this._wsSession = null;
742
- this._connectionStateHandler.resetConnectionState();
814
+ this._connectionStateHandler.handleDisconnected(DisconnectReasonEnum.CLIENT_INITIATED);
743
815
  this.clearAllManagers();
744
816
  }
745
817
  else if (this._room && this._room.state !== "disconnected") {
@@ -771,6 +843,7 @@ export class ConvaiClient extends EventEmitter {
771
843
  this._memoryManager = null; // Clear memory manager on disconnect
772
844
  this._audioManager.setWebSocketSession(null);
773
845
  this._audioManager.reset();
846
+ this._videoManager.setWebSocketVisionMessageSender(null);
774
847
  this._videoManager.reset();
775
848
  this._screenShareManager.reset();
776
849
  }
@@ -803,8 +876,9 @@ export class ConvaiClient extends EventEmitter {
803
876
  try {
804
877
  // Reset blendshape queue if mid-conversation, but don't interrupt the server
805
878
  if (this.blendshapeQueue.isConversationActive() ||
806
- this._state.isSpeaking) {
807
- this.blendshapeQueue.interrupt();
879
+ this._state.isSpeaking ||
880
+ this.blendshapeQueue.hasBufferedFrames()) {
881
+ this._messageHandler.interruptLipsyncPlayback();
808
882
  }
809
883
  // Start new conversation session
810
884
  this._conversationSessionId++;
@@ -831,8 +905,10 @@ export class ConvaiClient extends EventEmitter {
831
905
  if (!this.isTransportReady())
832
906
  return;
833
907
  // Reset blendshape queue if mid-conversation, but don't interrupt the server
834
- if (this.blendshapeQueue.isConversationActive() || this._state.isSpeaking) {
835
- this.blendshapeQueue.interrupt();
908
+ if (this.blendshapeQueue.isConversationActive() ||
909
+ this._state.isSpeaking ||
910
+ this.blendshapeQueue.hasBufferedFrames()) {
911
+ this._messageHandler.interruptLipsyncPlayback();
836
912
  }
837
913
  this._conversationSessionId++;
838
914
  this._conversationStartTime = Date.now();
@@ -853,7 +929,8 @@ export class ConvaiClient extends EventEmitter {
853
929
  * is clean before a new response starts arriving.
854
930
  */
855
931
  endCurrentConversationIfActive() {
856
- const queueActive = this.blendshapeQueue.isConversationActive();
932
+ const queueActive = this.blendshapeQueue.isConversationActive() ||
933
+ this.blendshapeQueue.hasBufferedFrames();
857
934
  const botSpeaking = this._state.isSpeaking;
858
935
  if (!queueActive && !botSpeaking)
859
936
  return;
@@ -863,7 +940,21 @@ export class ConvaiClient extends EventEmitter {
863
940
  });
864
941
  }
865
942
  // End the blendshape queue so the lipsync player finishes cleanly
866
- this.blendshapeQueue.interrupt();
943
+ this._messageHandler.interruptLipsyncPlayback();
944
+ }
945
+ startTrackedConversation(userMessage) {
946
+ if (this.blendshapeQueue.isConversationActive() ||
947
+ this._state.isSpeaking ||
948
+ this.blendshapeQueue.hasBufferedFrames()) {
949
+ this._messageHandler.interruptLipsyncPlayback();
950
+ }
951
+ this._conversationSessionId++;
952
+ this._conversationStartTime = Date.now();
953
+ this.emit("conversationStart", {
954
+ sessionId: this._conversationSessionId,
955
+ userMessage,
956
+ timestamp: this._conversationStartTime,
957
+ });
867
958
  }
868
959
  /**
869
960
  * Send an interrupt message to stop the bot's current response
@@ -875,14 +966,13 @@ export class ConvaiClient extends EventEmitter {
875
966
  this.publishMessage("interrupt-bot", {
876
967
  participant_sid: this._participantSid || this._room.localParticipant?.sid,
877
968
  });
878
- // Blendshape fade-out only for LiveKit (WebSocket transport has no lipsync;
879
- // calling interrupt() would set conversationEnded=true and block the next
880
- // bot-started-speaking from ever firing isSpeaking).
881
- if (this._activeTransport !== "websocket") {
882
- if (this.blendshapeQueue.isConversationActive() ||
883
- this._state.isSpeaking) {
884
- this.blendshapeQueue.interrupt();
885
- }
969
+ // Stop local lipsync immediately for every transport. WebSocket can also
970
+ // buffer ahead-delivered frames, and waiting for a server cancel leaves
971
+ // stale mouth animation visible after the audio stop.
972
+ if (this.blendshapeQueue.isConversationActive() ||
973
+ this._state.isSpeaking ||
974
+ this.blendshapeQueue.hasBufferedFrames()) {
975
+ this._messageHandler.interruptLipsyncPlayback();
886
976
  }
887
977
  }
888
978
  catch (error) {
@@ -947,36 +1037,97 @@ export class ConvaiClient extends EventEmitter {
947
1037
  console.warn("[ConvaiClient] updateContext: text or current_attention_object is required unless mode is 'reset'");
948
1038
  return;
949
1039
  }
950
- const willTriggerLlm = options.run_llm !== "false";
951
- if (willTriggerLlm) {
952
- if (this.blendshapeQueue.isConversationActive() ||
953
- this._state.isSpeaking) {
954
- this.blendshapeQueue.interrupt();
955
- }
956
- this._conversationSessionId++;
957
- this._conversationStartTime = Date.now();
958
- this.emit("conversationStart", {
959
- sessionId: this._conversationSessionId,
960
- userMessage: `[context-update:${options.mode ?? "append"}]`,
961
- timestamp: this._conversationStartTime,
962
- });
1040
+ // Only guaranteed-response updates should preempt active local lipsync.
1041
+ // Server-scheduled auto/silent updates may be downgraded while the bot is
1042
+ // speaking, so clearing the local queue here can stop lipsync while audio
1043
+ // continues.
1044
+ const shouldPreemptLipsync = shouldPreemptForContextUpdateResponse(options);
1045
+ if (shouldPreemptLipsync) {
1046
+ this.startTrackedConversation(`[context-update:${options.mode ?? "append"}]`);
963
1047
  }
964
1048
  this.publishMessage("context-update", {
965
1049
  ...(hasText && { text: options.text.trim() }),
966
1050
  ...(options.mode && { mode: options.mode }),
967
1051
  ...(options.run_llm && { run_llm: options.run_llm }),
1052
+ ...(options.respond_mode && { respond_mode: options.respond_mode }),
968
1053
  ...(hasAttention && { current_attention_object: options.current_attention_object }),
969
1054
  participant_sid: this._participantSid || this._room.localParticipant?.sid,
970
1055
  });
971
1056
  }
1057
+ visionStatus(options = {}) {
1058
+ if (!this.isTransportReady())
1059
+ return null;
1060
+ const updateId = options.updateId ?? this.createUpdateId("vision-status");
1061
+ this.publishMessage("vision-status", {
1062
+ update_id: updateId,
1063
+ participant_sid: this._participantSid || this._room.localParticipant?.sid,
1064
+ });
1065
+ return updateId;
1066
+ }
1067
+ visionTrigger(options = {}) {
1068
+ if (!this.isTransportReady())
1069
+ return null;
1070
+ const updateId = options.updateId ?? this.createUpdateId("vision-trigger");
1071
+ const respondMode = options.respondMode;
1072
+ if (shouldPreemptForExplicitRespondMode(respondMode)) {
1073
+ this.startTrackedConversation(`[vision-trigger:${respondMode}]`);
1074
+ }
1075
+ this.publishMessage("vision-trigger", {
1076
+ update_id: updateId,
1077
+ ...(options.text && { text: options.text }),
1078
+ ...(respondMode && { respond_mode: respondMode }),
1079
+ ...(options.frameIndices && { frame_indices: options.frameIndices }),
1080
+ ...(options.frameIds && { frame_ids: options.frameIds }),
1081
+ participant_sid: this._participantSid || this._room.localParticipant?.sid,
1082
+ });
1083
+ return updateId;
1084
+ }
1085
+ respondModeUpdate(options) {
1086
+ if (!this.isTransportReady())
1087
+ return null;
1088
+ const updateId = options.updateId ?? this.createUpdateId("respond-mode");
1089
+ this.publishMessage("respond-mode-update", {
1090
+ update_id: updateId,
1091
+ modality: options.modality,
1092
+ mode: options.mode,
1093
+ participant_sid: this._participantSid || this._room.localParticipant?.sid,
1094
+ });
1095
+ return updateId;
1096
+ }
972
1097
  /**
973
1098
  * Send descriptive scene updates. Use when environment description changes
974
1099
  * and the bot should know what is visible or nearby. Does not modify action_config.
975
1100
  */
976
- updateSceneMetadata(items) {
1101
+ updateSceneMetadata(items, options = {}) {
977
1102
  if (!this.isTransportReady())
978
1103
  return;
979
- this.publishMessage("update-scene-metadata", { scene_metadata: items });
1104
+ if (shouldPreemptForExplicitRespondMode(options.respondMode)) {
1105
+ this.startTrackedConversation(`[scene-metadata:${options.respondMode}]`);
1106
+ }
1107
+ this.publishMessage("update-scene-metadata", {
1108
+ scene_metadata: items,
1109
+ ...(options.respondMode && { respond_mode: options.respondMode }),
1110
+ });
1111
+ }
1112
+ createUpdateId(prefix) {
1113
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
1114
+ return `${prefix}:${crypto.randomUUID()}`;
1115
+ }
1116
+ return `${prefix}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
1117
+ }
1118
+ serializeRespondModes(config) {
1119
+ return {
1120
+ ...(config.text !== undefined && { text: config.text }),
1121
+ ...(config.audio !== undefined && { audio: config.audio }),
1122
+ ...(config.vision !== undefined && { vision: config.vision }),
1123
+ ...(config.contextUpdate !== undefined && {
1124
+ context_update: config.contextUpdate,
1125
+ }),
1126
+ ...(config.trigger !== undefined && { trigger: config.trigger }),
1127
+ ...(config.sceneMetadata !== undefined && {
1128
+ scene_metadata: config.sceneMetadata,
1129
+ }),
1130
+ };
980
1131
  }
981
1132
  /**
982
1133
  * Upload a file to the character via the LiveKit byte stream transport.