@lokutor/sdk 1.1.36 → 1.1.37

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/index.d.mts CHANGED
@@ -236,10 +236,14 @@ declare class VoiceAgentClient {
236
236
  private enableAudio;
237
237
  private currentGeneration;
238
238
  private listeners;
239
+ private configConfirmed;
239
240
  private isUserDisconnect;
240
241
  private reconnecting;
241
242
  private reconnectAttempts;
242
243
  private maxReconnectAttempts;
244
+ private reconnectTimer;
245
+ private serverPlaybackRate;
246
+ private serverInputRate;
243
247
  private serverUrl;
244
248
  constructor(config: LokutorConfig & {
245
249
  prompt: string;
@@ -346,6 +350,18 @@ declare class VoiceAgentClient {
346
350
  * Update the system prompt mid-conversation
347
351
  */
348
352
  updatePrompt(newPrompt: string): void;
353
+ /**
354
+ * Change the voice style mid-conversation
355
+ */
356
+ updateVoice(voice: VoiceStyle): void;
357
+ /**
358
+ * Change the language mid-conversation
359
+ */
360
+ updateLanguage(language: Language): void;
361
+ /**
362
+ * Update tool definitions mid-conversation
363
+ */
364
+ updateTools(tools: ToolDefinition[]): void;
349
365
  /**
350
366
  * Get full conversation transcript
351
367
  */
package/dist/index.d.ts CHANGED
@@ -236,10 +236,14 @@ declare class VoiceAgentClient {
236
236
  private enableAudio;
237
237
  private currentGeneration;
238
238
  private listeners;
239
+ private configConfirmed;
239
240
  private isUserDisconnect;
240
241
  private reconnecting;
241
242
  private reconnectAttempts;
242
243
  private maxReconnectAttempts;
244
+ private reconnectTimer;
245
+ private serverPlaybackRate;
246
+ private serverInputRate;
243
247
  private serverUrl;
244
248
  constructor(config: LokutorConfig & {
245
249
  prompt: string;
@@ -346,6 +350,18 @@ declare class VoiceAgentClient {
346
350
  * Update the system prompt mid-conversation
347
351
  */
348
352
  updatePrompt(newPrompt: string): void;
353
+ /**
354
+ * Change the voice style mid-conversation
355
+ */
356
+ updateVoice(voice: VoiceStyle): void;
357
+ /**
358
+ * Change the language mid-conversation
359
+ */
360
+ updateLanguage(language: Language): void;
361
+ /**
362
+ * Update tool definitions mid-conversation
363
+ */
364
+ updateTools(tools: ToolDefinition[]): void;
349
365
  /**
350
366
  * Get full conversation transcript
351
367
  */
package/dist/index.js CHANGED
@@ -659,11 +659,16 @@ var VoiceAgentClient = class {
659
659
  enableAudio = false;
660
660
  currentGeneration = 0;
661
661
  listeners = {};
662
+ configConfirmed = false;
662
663
  // Connection resilience
663
664
  isUserDisconnect = false;
664
665
  reconnecting = false;
665
666
  reconnectAttempts = 0;
666
667
  maxReconnectAttempts = 5;
668
+ reconnectTimer = null;
669
+ // Runtime state
670
+ serverPlaybackRate = 44100;
671
+ serverInputRate = 16e3;
667
672
  serverUrl;
668
673
  constructor(config) {
669
674
  this.apiKey = config.apiKey;
@@ -762,6 +767,7 @@ var VoiceAgentClient = class {
762
767
  };
763
768
  this.ws.onclose = (event) => {
764
769
  this.isConnected = false;
770
+ this.configConfirmed = false;
765
771
  const diagnostic = {
766
772
  code: event.code,
767
773
  reason: event.reason,
@@ -790,12 +796,19 @@ var VoiceAgentClient = class {
790
796
  const backoffDelay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 1e4);
791
797
  console.warn(`Connection lost. Reconnecting in ${backoffDelay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
792
798
  if (this.onStatus) this.onStatus("reconnecting");
793
- setTimeout(() => {
794
- this.connect().catch((e) => console.error("Reconnect failed", e));
799
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
800
+ this.reconnectTimer = setTimeout(() => {
801
+ this.reconnectTimer = null;
802
+ this.connect().then(() => {
803
+ this.emit("reconnected");
804
+ }).catch((e) => console.error("Reconnect failed", e));
795
805
  }, backoffDelay);
796
- } else {
806
+ } else if (this.isUserDisconnect) {
797
807
  console.log("Disconnected");
798
808
  if (this.onStatus) this.onStatus("disconnected");
809
+ } else {
810
+ console.log("Max reconnection attempts reached");
811
+ if (this.onStatus) this.onStatus("disconnected");
799
812
  }
800
813
  };
801
814
  } catch (err) {
@@ -827,9 +840,13 @@ var VoiceAgentClient = class {
827
840
  */
828
841
  sendConfig() {
829
842
  if (!this.ws || !this.isConnected) return;
830
- this.ws.send(JSON.stringify({ type: "visemes", data: this.wantVisemes }));
843
+ this.configConfirmed = false;
831
844
  this.ws.send(JSON.stringify({ type: "voice", data: this.voice }));
832
845
  this.ws.send(JSON.stringify({ type: "language", data: this.language }));
846
+ if (this.tools && this.tools.length > 0) {
847
+ this.ws.send(JSON.stringify({ type: "tools", data: this.tools }));
848
+ }
849
+ this.ws.send(JSON.stringify({ type: "visemes", data: this.wantVisemes }));
833
850
  this.ws.send(JSON.stringify({ type: "prompt", data: this.prompt }));
834
851
  this.ws.send(JSON.stringify({ type: "rates", playback: 44100, input: 16e3 }));
835
852
  sdkTrace("ws.send.config", {
@@ -839,9 +856,6 @@ var VoiceAgentClient = class {
839
856
  visemes: this.wantVisemes,
840
857
  tools: this.tools?.length || 0
841
858
  });
842
- if (this.tools && this.tools.length > 0) {
843
- this.ws.send(JSON.stringify({ type: "tools", data: this.tools }));
844
- }
845
859
  console.log(`\u2699\uFE0F Configured: voice=${this.voice}, language=${this.language}, visemes=${this.wantVisemes}, tools=${this.tools.length}`);
846
860
  }
847
861
  /**
@@ -888,6 +902,11 @@ var VoiceAgentClient = class {
888
902
  this.handleBinaryMessage(buffer, msg.generation);
889
903
  }
890
904
  break;
905
+ case "server_rates":
906
+ if (msg.playback) this.serverPlaybackRate = msg.playback;
907
+ if (msg.input) this.serverInputRate = msg.input;
908
+ sdkTrace("server_rates", { playback: this.serverPlaybackRate, input: this.serverInputRate });
909
+ break;
891
910
  case "transcript":
892
911
  const role = msg.role === "user" ? "user" : "agent";
893
912
  this.messages.push({
@@ -906,11 +925,17 @@ var VoiceAgentClient = class {
906
925
  }
907
926
  break;
908
927
  case "status":
928
+ if (msg.data === "prompt_set") {
929
+ this.configConfirmed = true;
930
+ }
909
931
  if (msg.data === "thinking") {
910
932
  const newGen = msg.generation || 0;
911
933
  if (newGen > this.currentGeneration) {
912
934
  console.log(`\u{1F9E0} New thought (Gen ${newGen})`);
913
935
  this.currentGeneration = newGen;
936
+ if (this.audioManager) {
937
+ this.audioManager.stopPlayback();
938
+ }
914
939
  }
915
940
  }
916
941
  if (msg.data === "interrupted" && this.audioManager) {
@@ -1021,6 +1046,10 @@ var VoiceAgentClient = class {
1021
1046
  */
1022
1047
  disconnect() {
1023
1048
  this.isUserDisconnect = true;
1049
+ if (this.reconnectTimer) {
1050
+ clearTimeout(this.reconnectTimer);
1051
+ this.reconnectTimer = null;
1052
+ }
1024
1053
  if (this.ws) {
1025
1054
  this.ws.close();
1026
1055
  this.ws = null;
@@ -1029,6 +1058,7 @@ var VoiceAgentClient = class {
1029
1058
  this.audioManager.cleanup();
1030
1059
  }
1031
1060
  this.isConnected = false;
1061
+ this.configConfirmed = false;
1032
1062
  this.reconnecting = false;
1033
1063
  this.reconnectAttempts = 0;
1034
1064
  }
@@ -1135,6 +1165,36 @@ var VoiceAgentClient = class {
1135
1165
  console.warn("Not connected - prompt will be updated on next connection");
1136
1166
  }
1137
1167
  }
1168
+ /**
1169
+ * Change the voice style mid-conversation
1170
+ */
1171
+ updateVoice(voice) {
1172
+ this.voice = voice;
1173
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
1174
+ this.ws.send(JSON.stringify({ type: "voice", data: voice }));
1175
+ console.log(`\u2699\uFE0F Updated voice: ${voice}`);
1176
+ }
1177
+ }
1178
+ /**
1179
+ * Change the language mid-conversation
1180
+ */
1181
+ updateLanguage(language) {
1182
+ this.language = language;
1183
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
1184
+ this.ws.send(JSON.stringify({ type: "language", data: language }));
1185
+ console.log(`\u2699\uFE0F Updated language: ${language}`);
1186
+ }
1187
+ }
1188
+ /**
1189
+ * Update tool definitions mid-conversation
1190
+ */
1191
+ updateTools(tools) {
1192
+ this.tools = tools;
1193
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
1194
+ this.ws.send(JSON.stringify({ type: "tools", data: tools }));
1195
+ console.log(`\u2699\uFE0F Updated ${tools.length} tool(s)`);
1196
+ }
1197
+ }
1138
1198
  /**
1139
1199
  * Get full conversation transcript
1140
1200
  */
package/dist/index.mjs CHANGED
@@ -612,11 +612,16 @@ var VoiceAgentClient = class {
612
612
  enableAudio = false;
613
613
  currentGeneration = 0;
614
614
  listeners = {};
615
+ configConfirmed = false;
615
616
  // Connection resilience
616
617
  isUserDisconnect = false;
617
618
  reconnecting = false;
618
619
  reconnectAttempts = 0;
619
620
  maxReconnectAttempts = 5;
621
+ reconnectTimer = null;
622
+ // Runtime state
623
+ serverPlaybackRate = 44100;
624
+ serverInputRate = 16e3;
620
625
  serverUrl;
621
626
  constructor(config) {
622
627
  this.apiKey = config.apiKey;
@@ -715,6 +720,7 @@ var VoiceAgentClient = class {
715
720
  };
716
721
  this.ws.onclose = (event) => {
717
722
  this.isConnected = false;
723
+ this.configConfirmed = false;
718
724
  const diagnostic = {
719
725
  code: event.code,
720
726
  reason: event.reason,
@@ -743,12 +749,19 @@ var VoiceAgentClient = class {
743
749
  const backoffDelay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 1e4);
744
750
  console.warn(`Connection lost. Reconnecting in ${backoffDelay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
745
751
  if (this.onStatus) this.onStatus("reconnecting");
746
- setTimeout(() => {
747
- this.connect().catch((e) => console.error("Reconnect failed", e));
752
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
753
+ this.reconnectTimer = setTimeout(() => {
754
+ this.reconnectTimer = null;
755
+ this.connect().then(() => {
756
+ this.emit("reconnected");
757
+ }).catch((e) => console.error("Reconnect failed", e));
748
758
  }, backoffDelay);
749
- } else {
759
+ } else if (this.isUserDisconnect) {
750
760
  console.log("Disconnected");
751
761
  if (this.onStatus) this.onStatus("disconnected");
762
+ } else {
763
+ console.log("Max reconnection attempts reached");
764
+ if (this.onStatus) this.onStatus("disconnected");
752
765
  }
753
766
  };
754
767
  } catch (err) {
@@ -780,9 +793,13 @@ var VoiceAgentClient = class {
780
793
  */
781
794
  sendConfig() {
782
795
  if (!this.ws || !this.isConnected) return;
783
- this.ws.send(JSON.stringify({ type: "visemes", data: this.wantVisemes }));
796
+ this.configConfirmed = false;
784
797
  this.ws.send(JSON.stringify({ type: "voice", data: this.voice }));
785
798
  this.ws.send(JSON.stringify({ type: "language", data: this.language }));
799
+ if (this.tools && this.tools.length > 0) {
800
+ this.ws.send(JSON.stringify({ type: "tools", data: this.tools }));
801
+ }
802
+ this.ws.send(JSON.stringify({ type: "visemes", data: this.wantVisemes }));
786
803
  this.ws.send(JSON.stringify({ type: "prompt", data: this.prompt }));
787
804
  this.ws.send(JSON.stringify({ type: "rates", playback: 44100, input: 16e3 }));
788
805
  sdkTrace("ws.send.config", {
@@ -792,9 +809,6 @@ var VoiceAgentClient = class {
792
809
  visemes: this.wantVisemes,
793
810
  tools: this.tools?.length || 0
794
811
  });
795
- if (this.tools && this.tools.length > 0) {
796
- this.ws.send(JSON.stringify({ type: "tools", data: this.tools }));
797
- }
798
812
  console.log(`\u2699\uFE0F Configured: voice=${this.voice}, language=${this.language}, visemes=${this.wantVisemes}, tools=${this.tools.length}`);
799
813
  }
800
814
  /**
@@ -841,6 +855,11 @@ var VoiceAgentClient = class {
841
855
  this.handleBinaryMessage(buffer, msg.generation);
842
856
  }
843
857
  break;
858
+ case "server_rates":
859
+ if (msg.playback) this.serverPlaybackRate = msg.playback;
860
+ if (msg.input) this.serverInputRate = msg.input;
861
+ sdkTrace("server_rates", { playback: this.serverPlaybackRate, input: this.serverInputRate });
862
+ break;
844
863
  case "transcript":
845
864
  const role = msg.role === "user" ? "user" : "agent";
846
865
  this.messages.push({
@@ -859,11 +878,17 @@ var VoiceAgentClient = class {
859
878
  }
860
879
  break;
861
880
  case "status":
881
+ if (msg.data === "prompt_set") {
882
+ this.configConfirmed = true;
883
+ }
862
884
  if (msg.data === "thinking") {
863
885
  const newGen = msg.generation || 0;
864
886
  if (newGen > this.currentGeneration) {
865
887
  console.log(`\u{1F9E0} New thought (Gen ${newGen})`);
866
888
  this.currentGeneration = newGen;
889
+ if (this.audioManager) {
890
+ this.audioManager.stopPlayback();
891
+ }
867
892
  }
868
893
  }
869
894
  if (msg.data === "interrupted" && this.audioManager) {
@@ -974,6 +999,10 @@ var VoiceAgentClient = class {
974
999
  */
975
1000
  disconnect() {
976
1001
  this.isUserDisconnect = true;
1002
+ if (this.reconnectTimer) {
1003
+ clearTimeout(this.reconnectTimer);
1004
+ this.reconnectTimer = null;
1005
+ }
977
1006
  if (this.ws) {
978
1007
  this.ws.close();
979
1008
  this.ws = null;
@@ -982,6 +1011,7 @@ var VoiceAgentClient = class {
982
1011
  this.audioManager.cleanup();
983
1012
  }
984
1013
  this.isConnected = false;
1014
+ this.configConfirmed = false;
985
1015
  this.reconnecting = false;
986
1016
  this.reconnectAttempts = 0;
987
1017
  }
@@ -1088,6 +1118,36 @@ var VoiceAgentClient = class {
1088
1118
  console.warn("Not connected - prompt will be updated on next connection");
1089
1119
  }
1090
1120
  }
1121
+ /**
1122
+ * Change the voice style mid-conversation
1123
+ */
1124
+ updateVoice(voice) {
1125
+ this.voice = voice;
1126
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
1127
+ this.ws.send(JSON.stringify({ type: "voice", data: voice }));
1128
+ console.log(`\u2699\uFE0F Updated voice: ${voice}`);
1129
+ }
1130
+ }
1131
+ /**
1132
+ * Change the language mid-conversation
1133
+ */
1134
+ updateLanguage(language) {
1135
+ this.language = language;
1136
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
1137
+ this.ws.send(JSON.stringify({ type: "language", data: language }));
1138
+ console.log(`\u2699\uFE0F Updated language: ${language}`);
1139
+ }
1140
+ }
1141
+ /**
1142
+ * Update tool definitions mid-conversation
1143
+ */
1144
+ updateTools(tools) {
1145
+ this.tools = tools;
1146
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
1147
+ this.ws.send(JSON.stringify({ type: "tools", data: tools }));
1148
+ console.log(`\u2699\uFE0F Updated ${tools.length} tool(s)`);
1149
+ }
1150
+ }
1091
1151
  /**
1092
1152
  * Get full conversation transcript
1093
1153
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lokutor/sdk",
3
3
 
4
- "version": "1.1.36",
4
+ "version": "1.1.37",
5
5
  "description": "JavaScript/TypeScript SDK for Lokutor Real-time Voice AI",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
package/src/client.ts CHANGED
@@ -166,12 +166,18 @@ export class VoiceAgentClient {
166
166
  private enableAudio: boolean = false;
167
167
  private currentGeneration: number = 0;
168
168
  private listeners: Record<string, Function[]> = {};
169
+ private configConfirmed: boolean = false;
169
170
 
170
171
  // Connection resilience
171
172
  private isUserDisconnect: boolean = false;
172
173
  private reconnecting: boolean = false;
173
174
  private reconnectAttempts: number = 0;
174
175
  private maxReconnectAttempts: number = 5;
176
+ private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
177
+
178
+ // Runtime state
179
+ private serverPlaybackRate: number = 44100;
180
+ private serverInputRate: number = 16000;
175
181
 
176
182
  private serverUrl: string;
177
183
 
@@ -296,6 +302,7 @@ export class VoiceAgentClient {
296
302
 
297
303
  this.ws.onclose = (event) => {
298
304
  this.isConnected = false;
305
+ this.configConfirmed = false;
299
306
  const diagnostic = {
300
307
  code: event.code,
301
308
  reason: event.reason,
@@ -331,12 +338,19 @@ export class VoiceAgentClient {
331
338
 
332
339
  if (this.onStatus) this.onStatus('reconnecting');
333
340
 
334
- setTimeout(() => {
335
- this.connect().catch(e => console.error("Reconnect failed", e));
341
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
342
+ this.reconnectTimer = setTimeout(() => {
343
+ this.reconnectTimer = null;
344
+ this.connect().then(() => {
345
+ this.emit('reconnected');
346
+ }).catch(e => console.error("Reconnect failed", e));
336
347
  }, backoffDelay);
337
- } else {
348
+ } else if (this.isUserDisconnect) {
338
349
  console.log('Disconnected');
339
350
  if (this.onStatus) this.onStatus('disconnected');
351
+ } else {
352
+ console.log('Max reconnection attempts reached');
353
+ if (this.onStatus) this.onStatus('disconnected');
340
354
  }
341
355
  };
342
356
 
@@ -374,14 +388,18 @@ export class VoiceAgentClient {
374
388
  private sendConfig() {
375
389
  if (!this.ws || !this.isConnected) return;
376
390
 
377
- // Send feature/config flags first so the first generated response uses them.
378
- this.ws.send(JSON.stringify({ type: 'visemes', data: this.wantVisemes }));
391
+ this.configConfirmed = false;
392
+
393
+ // Send feature/config flags — order matches the test agent for consistency.
379
394
  this.ws.send(JSON.stringify({ type: 'voice', data: this.voice }));
380
395
  this.ws.send(JSON.stringify({ type: 'language', data: this.language }));
396
+ if (this.tools && this.tools.length > 0) {
397
+ this.ws.send(JSON.stringify({ type: 'tools', data: this.tools }));
398
+ }
399
+ this.ws.send(JSON.stringify({ type: 'visemes', data: this.wantVisemes }));
381
400
  this.ws.send(JSON.stringify({ type: 'prompt', data: this.prompt }));
382
401
 
383
402
  // Inform the server of our sample rates for echo cancellation.
384
- // These match the SDK defaults: 16 kHz mic input, 44.1 kHz speaker output.
385
403
  this.ws.send(JSON.stringify({ type: 'rates', playback: 44100, input: 16000 }));
386
404
 
387
405
  sdkTrace('ws.send.config', {
@@ -392,10 +410,6 @@ export class VoiceAgentClient {
392
410
  tools: this.tools?.length || 0
393
411
  });
394
412
 
395
- if (this.tools && this.tools.length > 0) {
396
- this.ws.send(JSON.stringify({ type: 'tools', data: this.tools }));
397
- }
398
-
399
413
  console.log(`⚙️ Configured: voice=${this.voice}, language=${this.language}, visemes=${this.wantVisemes}, tools=${this.tools.length}`);
400
414
  }
401
415
 
@@ -445,6 +459,11 @@ export class VoiceAgentClient {
445
459
  this.handleBinaryMessage(buffer, msg.generation);
446
460
  }
447
461
  break;
462
+ case 'server_rates':
463
+ if (msg.playback) this.serverPlaybackRate = msg.playback;
464
+ if (msg.input) this.serverInputRate = msg.input;
465
+ sdkTrace('server_rates', { playback: this.serverPlaybackRate, input: this.serverInputRate });
466
+ break;
448
467
  case 'transcript':
449
468
  const role = msg.role === 'user' ? 'user' : 'agent';
450
469
  // Store in history
@@ -465,12 +484,18 @@ export class VoiceAgentClient {
465
484
  }
466
485
  break;
467
486
  case 'status':
487
+ if (msg.data === 'prompt_set') {
488
+ this.configConfirmed = true;
489
+ }
468
490
  if (msg.data === 'thinking') {
469
491
  const newGen = msg.generation || 0;
470
- if (newGen > this.currentGeneration) {
471
- console.log(`🧠 New thought (Gen ${newGen})`);
472
- this.currentGeneration = newGen;
473
- }
492
+ if (newGen > this.currentGeneration) {
493
+ console.log(`🧠 New thought (Gen ${newGen})`);
494
+ this.currentGeneration = newGen;
495
+ if (this.audioManager) {
496
+ this.audioManager.stopPlayback();
497
+ }
498
+ }
474
499
  }
475
500
  if (msg.data === 'interrupted' && this.audioManager) {
476
501
  this.audioManager.stopPlayback();
@@ -589,6 +614,10 @@ export class VoiceAgentClient {
589
614
  */
590
615
  public disconnect() {
591
616
  this.isUserDisconnect = true;
617
+ if (this.reconnectTimer) {
618
+ clearTimeout(this.reconnectTimer);
619
+ this.reconnectTimer = null;
620
+ }
592
621
  if (this.ws) {
593
622
  this.ws.close();
594
623
  this.ws = null;
@@ -597,6 +626,7 @@ export class VoiceAgentClient {
597
626
  this.audioManager.cleanup();
598
627
  }
599
628
  this.isConnected = false;
629
+ this.configConfirmed = false;
600
630
  this.reconnecting = false;
601
631
  this.reconnectAttempts = 0;
602
632
  }
@@ -715,6 +745,39 @@ export class VoiceAgentClient {
715
745
  }
716
746
  }
717
747
 
748
+ /**
749
+ * Change the voice style mid-conversation
750
+ */
751
+ public updateVoice(voice: VoiceStyle) {
752
+ this.voice = voice;
753
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
754
+ this.ws.send(JSON.stringify({ type: 'voice', data: voice }));
755
+ console.log(`⚙️ Updated voice: ${voice}`);
756
+ }
757
+ }
758
+
759
+ /**
760
+ * Change the language mid-conversation
761
+ */
762
+ public updateLanguage(language: Language) {
763
+ this.language = language;
764
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
765
+ this.ws.send(JSON.stringify({ type: 'language', data: language }));
766
+ console.log(`⚙️ Updated language: ${language}`);
767
+ }
768
+ }
769
+
770
+ /**
771
+ * Update tool definitions mid-conversation
772
+ */
773
+ public updateTools(tools: ToolDefinition[]) {
774
+ this.tools = tools;
775
+ if (this.ws && this.ws.readyState === WebSocket.OPEN && this.isConnected) {
776
+ this.ws.send(JSON.stringify({ type: 'tools', data: tools }));
777
+ console.log(`⚙️ Updated ${tools.length} tool(s)`);
778
+ }
779
+ }
780
+
718
781
  /**
719
782
  * Get full conversation transcript
720
783
  */