kugelaudio 0.8.0 → 0.9.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/index.d.ts CHANGED
@@ -34,6 +34,7 @@ interface Voice {
34
34
  category?: VoiceCategory;
35
35
  sex?: VoiceSex;
36
36
  age?: VoiceAge;
37
+ quality?: string;
37
38
  supportedLanguages: string[];
38
39
  sampleText?: string;
39
40
  avatarUrl?: string;
@@ -221,7 +222,7 @@ interface GenerateOptions {
221
222
  modelId?: string;
222
223
  /** Voice ID to use */
223
224
  voiceId?: number;
224
- /** CFG scale for generation (default: 2.0) */
225
+ /** Classifier-free guidance scale. Clamped to [1.2, 2.5] (default: 2.0). */
225
226
  cfgScale?: number;
226
227
  /**
227
228
  * Sampling variance. Range [0.0, 1.0]. 0 = most stable (near-greedy),
@@ -269,8 +270,9 @@ interface GenerateOptions {
269
270
  /**
270
271
  * Playback speed multiplier (0.8 = slower, 1.0 = normal, 1.2 = faster).
271
272
  *
272
- * Uses pitch-preserving time-stretching (WSOLA); applies uniformly to the
273
- * whole request (no per-span control).
273
+ * Uses pitch-preserving time-stretching (WSOLA); applies to the whole
274
+ * request. Wrap text in `<prosody rate="slow|medium|fast|0.8-1.2">` to
275
+ * override the rate for a span (the span rate wins inside the span).
274
276
  * Range: [0.8, 1.2]. Default: 1.0.
275
277
  */
276
278
  speed?: number;
@@ -312,7 +314,7 @@ interface StreamConfig {
312
314
  voiceId?: number;
313
315
  /** Model ID. Default: 'kugel-3'. Legacy ids still accepted; they alias to kugel-3 server-side. */
314
316
  modelId?: string;
315
- /** CFG scale for generation */
317
+ /** Classifier-free guidance scale. Clamped to [1.2, 2.5] (default: 2.0). */
316
318
  cfgScale?: number;
317
319
  /**
318
320
  * Sampling variance. Range [0.0, 1.0]. 0 = most stable, 1 = most variance.
@@ -369,8 +371,9 @@ interface StreamConfig {
369
371
  /**
370
372
  * Playback speed multiplier (0.8 = slower, 1.0 = normal, 1.2 = faster).
371
373
  *
372
- * Uses pitch-preserving time-stretching (WSOLA); applies uniformly to the
373
- * whole request (no per-span control).
374
+ * Uses pitch-preserving time-stretching (WSOLA); applies to the whole
375
+ * request. Wrap text in `<prosody rate="slow|medium|fast|0.8-1.2">` to
376
+ * override the rate for a span (the span rate wins inside the span).
374
377
  * Range: [0.8, 1.2]. Default: 1.0.
375
378
  */
376
379
  speed?: number;
@@ -383,6 +386,41 @@ interface StreamConfig {
383
386
  */
384
387
  dictionaryIds?: number[];
385
388
  }
389
+ /**
390
+ * Generation parameters changeable mid-connection via
391
+ * {@link StreamingSession.updateSettings} / {@link MultiContextSession.updateSettings}
392
+ * (KUG-1166). Every field is optional; an update changes only the fields it
393
+ * carries. Identity / audio-format fields (`voiceId`, `modelId`, `sampleRate`,
394
+ * `outputFormat`, `dictionaryIds`) are NOT here — they are fixed for the
395
+ * connection's lifetime and the server rejects them in an update.
396
+ */
397
+ interface SettingsUpdate {
398
+ /** Classifier-free guidance scale (0.0–10.0). */
399
+ cfgScale?: number;
400
+ /** Sampling variance (0.0–1.0). */
401
+ temperature?: number;
402
+ /** Playback speed multiplier (0.8–1.2). */
403
+ speed?: number;
404
+ /** Maximum tokens per generation (1–8192). */
405
+ maxNewTokens?: number;
406
+ /** Language code for normalization (e.g. `'de'`). */
407
+ language?: string;
408
+ /** Enable text normalization. */
409
+ normalize?: boolean;
410
+ }
411
+ /**
412
+ * The generation parameters in effect after an
413
+ * {@link StreamingSession.updateSettings} call — the server's echo. Fields the
414
+ * session never set come back `null` (e.g. `temperature`, `language`).
415
+ */
416
+ interface EffectiveSettings {
417
+ cfgScale?: number;
418
+ temperature?: number | null;
419
+ speed?: number;
420
+ maxNewTokens?: number;
421
+ language?: string | null;
422
+ normalize?: boolean;
423
+ }
386
424
  /**
387
425
  * Event callbacks for a streaming session (`/ws/tts/stream`).
388
426
  *
@@ -577,7 +615,7 @@ interface MultiContextConfig {
577
615
  sampleRate?: number;
578
616
  /** Combined codec+rate token (e.g. 'ulaw_8000'); opt-in, set-once per context. */
579
617
  outputFormat?: string;
580
- /** CFG scale for generation (default: 2.0) */
618
+ /** Classifier-free guidance scale. Clamped to [1.2, 2.5] (default: 2.0). */
581
619
  cfgScale?: number;
582
620
  /**
583
621
  * Sampling variance. Range [0.0, 1.0]. 0 = most stable, 1 = most variance.
@@ -1061,6 +1099,22 @@ declare class MultiContextSession {
1061
1099
  * Send keep-alive to reset a context's inactivity timeout.
1062
1100
  */
1063
1101
  keepAlive(contextId: string): void;
1102
+ /**
1103
+ * Change the session's generation parameters mid-connection (KUG-1166).
1104
+ *
1105
+ * Session-scoped (there is no `contextId`): the update applies to contexts
1106
+ * started **after** it — a context already streaming keeps the settings it
1107
+ * began with, since generation parameters are bound when a context's engine
1108
+ * session opens. With the common one-context-per-turn pattern that means it
1109
+ * takes effect on the next turn. Per-context `cfgScale` / `maxNewTokens`
1110
+ * passed to {@link createContext} still win for that context.
1111
+ *
1112
+ * Resolves with the generation parameters now in effect (the server's echo).
1113
+ *
1114
+ * @throws {KugelAudioError} if no field is provided, the socket is not open,
1115
+ * or the server rejects the update.
1116
+ */
1117
+ updateSettings(settings: SettingsUpdate): Promise<EffectiveSettings>;
1064
1118
  /**
1065
1119
  * Close the session and all contexts.
1066
1120
  */
@@ -1190,6 +1244,28 @@ declare class StreamingSession {
1190
1244
  * to change voice, model, language, or other settings.
1191
1245
  */
1192
1246
  updateConfig(config: Partial<StreamConfig>): void;
1247
+ /**
1248
+ * Change generation parameters mid-connection without reconnecting (KUG-1166).
1249
+ *
1250
+ * Sends an `update_settings` message and resolves with the generation
1251
+ * parameters now in effect (the server's echo). Only the six fields of
1252
+ * {@link SettingsUpdate} are updatable; identity / audio-format fields
1253
+ * (`voiceId`, `modelId`, `sampleRate`, `outputFormat`, `dictionaryIds`) are
1254
+ * fixed for the connection — change those with {@link updateConfig} after
1255
+ * {@link endSession} instead.
1256
+ *
1257
+ * The change applies to the **next turn**: a turn already streaming keeps the
1258
+ * settings it started with, so call this between turns.
1259
+ *
1260
+ * @example
1261
+ * ```typescript
1262
+ * const effective = await session.updateSettings({ cfgScale: 1.5, speed: 1.1 });
1263
+ * ```
1264
+ *
1265
+ * @throws {KugelAudioError} if no field is provided, the socket is not open,
1266
+ * or the server rejects the update (e.g. a value out of range).
1267
+ */
1268
+ updateSettings(settings: SettingsUpdate): Promise<EffectiveSettings>;
1193
1269
  /**
1194
1270
  * Close the session and the WebSocket connection.
1195
1271
  *
@@ -1445,9 +1521,6 @@ declare function classifyWsClose(code: number | undefined, reason?: string): Kug
1445
1521
  */
1446
1522
  declare function classifyWsHandshakeError(err: unknown): KugelAudioError | null;
1447
1523
 
1448
- /**
1449
- * Utility functions for KugelAudio SDK.
1450
- */
1451
1524
  /**
1452
1525
  * Decode base64 string to ArrayBuffer.
1453
1526
  */
@@ -1465,4 +1538,4 @@ declare function createWavFile(audio: ArrayBuffer, sampleRate: number): ArrayBuf
1465
1538
  */
1466
1539
  declare function createWavBlob(audio: ArrayBuffer, sampleRate: number): Blob;
1467
1540
 
1468
- export { type AudioChunk, type AudioResponse, AuthenticationError, type BulkReplaceResult, ConnectionError, type ContextVoiceSettings, type CreateDictionaryOptions, type CreateVoiceOptions, DictionariesResource, type Dictionary, DictionaryEntriesResource, type DictionaryEntry, type DictionaryEntryInput, type DictionaryEntryListResponse, type ErrorCode, ErrorCodes, type GenerateOptions, type GenerationStats, InsufficientCreditsError, KugelAudio, KugelAudioError, type KugelAudioErrorOptions, type KugelAudioOptions, type Model, type MultiContextAudioChunk, type MultiContextCallbacks, type MultiContextConfig, NotFoundError, RateLimitError, type Region, type SessionUsage, type StreamCallbacks, type StreamConfig, type StreamingSessionCallbacks, type UpdateDictionaryEntryOptions, type UpdateDictionaryOptions, type UpdateVoiceOptions, ValidationError, type Voice, type VoiceAge, type VoiceCategory, type VoiceDetail, type VoiceListResponse, type VoiceQuality, type VoiceReference, type VoiceSex, type WordTimestamp, WsCloseCodes, base64ToArrayBuffer, classifyHttpError, classifyWsClose, classifyWsFrame, classifyWsHandshakeError, createWavBlob, createWavFile, decodePCM16, parseSessionUsage };
1541
+ export { type AudioChunk, type AudioResponse, AuthenticationError, type BulkReplaceResult, ConnectionError, type ContextVoiceSettings, type CreateDictionaryOptions, type CreateVoiceOptions, DictionariesResource, type Dictionary, DictionaryEntriesResource, type DictionaryEntry, type DictionaryEntryInput, type DictionaryEntryListResponse, type EffectiveSettings, type ErrorCode, ErrorCodes, type GenerateOptions, type GenerationStats, InsufficientCreditsError, KugelAudio, KugelAudioError, type KugelAudioErrorOptions, type KugelAudioOptions, type Model, type MultiContextAudioChunk, type MultiContextCallbacks, type MultiContextConfig, NotFoundError, RateLimitError, type Region, type SessionUsage, type SettingsUpdate, type StreamCallbacks, type StreamConfig, type StreamingSessionCallbacks, type UpdateDictionaryEntryOptions, type UpdateDictionaryOptions, type UpdateVoiceOptions, ValidationError, type Voice, type VoiceAge, type VoiceCategory, type VoiceDetail, type VoiceListResponse, type VoiceQuality, type VoiceReference, type VoiceSex, type WordTimestamp, WsCloseCodes, base64ToArrayBuffer, classifyHttpError, classifyWsClose, classifyWsFrame, classifyWsHandshakeError, createWavBlob, createWavFile, decodePCM16, parseSessionUsage };
package/dist/index.js CHANGED
@@ -448,6 +448,12 @@ function parseSessionUsage(data) {
448
448
  }
449
449
 
450
450
  // src/utils.ts
451
+ var MIN_CFG_SCALE = 1.2;
452
+ var MAX_CFG_SCALE = 2.5;
453
+ function clampCfgScale(cfgScale) {
454
+ if (cfgScale === void 0) return void 0;
455
+ return Math.min(MAX_CFG_SCALE, Math.max(MIN_CFG_SCALE, cfgScale));
456
+ }
451
457
  function base64ToArrayBuffer(base64) {
452
458
  if (typeof atob === "function") {
453
459
  const binary = atob(base64);
@@ -533,7 +539,7 @@ function getWebSocket() {
533
539
  // package.json
534
540
  var package_default = {
535
541
  name: "kugelaudio",
536
- version: "0.8.0",
542
+ version: "0.9.0",
537
543
  description: "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
538
544
  main: "dist/index.js",
539
545
  module: "dist/index.mjs",
@@ -543,6 +549,11 @@ var package_default = {
543
549
  types: "./dist/index.d.ts",
544
550
  import: "./dist/index.mjs",
545
551
  require: "./dist/index.js"
552
+ },
553
+ "./livekit": {
554
+ types: "./dist/livekit/index.d.ts",
555
+ import: "./dist/livekit/index.mjs",
556
+ require: "./dist/livekit/index.js"
546
557
  }
547
558
  },
548
559
  files: [
@@ -552,8 +563,8 @@ var package_default = {
552
563
  "CHANGELOG.md"
553
564
  ],
554
565
  scripts: {
555
- build: "tsup src/index.ts --format cjs,esm --dts",
556
- dev: "tsup src/index.ts --format cjs,esm --dts --watch",
566
+ build: "tsup src/index.ts src/livekit/index.ts --format cjs,esm --dts",
567
+ dev: "tsup src/index.ts src/livekit/index.ts --format cjs,esm --dts --watch",
557
568
  lint: "eslint src/",
558
569
  test: "vitest run",
559
570
  "test:watch": "vitest",
@@ -579,11 +590,26 @@ var package_default = {
579
590
  url: "https://github.com/Kugelaudio/KugelAudio/issues"
580
591
  },
581
592
  devDependencies: {
593
+ "@livekit/agents": "^1.4.11",
594
+ "@livekit/rtc-node": "^0.13.30",
582
595
  "@types/node": "^25.3.2",
596
+ "@types/ws": "^8.18.1",
583
597
  tsup: "^8.0.0",
584
598
  typescript: "^6.0.2",
585
599
  vitest: "^4.0.18"
586
600
  },
601
+ peerDependencies: {
602
+ "@livekit/agents": ">=1.0.0",
603
+ "@livekit/rtc-node": ">=0.13.0"
604
+ },
605
+ peerDependenciesMeta: {
606
+ "@livekit/agents": {
607
+ optional: true
608
+ },
609
+ "@livekit/rtc-node": {
610
+ optional: true
611
+ }
612
+ },
587
613
  engines: {
588
614
  node: ">=18.0.0"
589
615
  },
@@ -623,6 +649,105 @@ function createWs(url) {
623
649
  return new WS(url);
624
650
  }
625
651
  var WS_OPEN = 1;
652
+ var UPDATABLE_SETTINGS = {
653
+ cfgScale: "cfg_scale",
654
+ temperature: "temperature",
655
+ speed: "speed",
656
+ maxNewTokens: "max_new_tokens",
657
+ language: "language",
658
+ normalize: "normalize"
659
+ };
660
+ function buildSettingsUpdateBody(settings) {
661
+ const body = {};
662
+ for (const camel of Object.keys(UPDATABLE_SETTINGS)) {
663
+ const value = settings[camel];
664
+ if (value !== void 0) body[UPDATABLE_SETTINGS[camel]] = value;
665
+ }
666
+ if (Object.keys(body).length === 0) {
667
+ throw new KugelAudioError(
668
+ `updateSettings requires at least one parameter to change (one of ${Object.keys(UPDATABLE_SETTINGS).join(", ")})`
669
+ );
670
+ }
671
+ return body;
672
+ }
673
+ function parseEffectiveSettings(raw) {
674
+ const s = raw && typeof raw === "object" ? raw : {};
675
+ return {
676
+ cfgScale: s.cfg_scale,
677
+ temperature: s.temperature,
678
+ speed: s.speed,
679
+ maxNewTokens: s.max_new_tokens,
680
+ language: s.language,
681
+ normalize: s.normalize
682
+ };
683
+ }
684
+ function applyAcceptedSettingsUpdate(config, settings) {
685
+ for (const camel of Object.keys(UPDATABLE_SETTINGS)) {
686
+ if (settings[camel] !== void 0) {
687
+ config[camel] = settings[camel];
688
+ }
689
+ }
690
+ }
691
+ function sendUpdateSettings(ws, body) {
692
+ const QUIET_TIMEOUT_MS = 15e3;
693
+ return new Promise((resolve, reject) => {
694
+ let settled = false;
695
+ let timer;
696
+ const prevMessage = ws.onmessage;
697
+ const prevClose = ws.onclose;
698
+ const finish = (action) => {
699
+ if (settled) return;
700
+ settled = true;
701
+ clearTimeout(timer);
702
+ ws.onmessage = prevMessage;
703
+ ws.onclose = prevClose;
704
+ action();
705
+ };
706
+ const armQuietTimer = () => {
707
+ clearTimeout(timer);
708
+ timer = setTimeout(
709
+ () => finish(
710
+ () => reject(
711
+ new KugelAudioError(
712
+ "Timed out waiting for settings_updated acknowledgement"
713
+ )
714
+ )
715
+ ),
716
+ QUIET_TIMEOUT_MS
717
+ );
718
+ };
719
+ armQuietTimer();
720
+ ws.onmessage = (event) => {
721
+ armQuietTimer();
722
+ let data = null;
723
+ try {
724
+ const raw = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
725
+ data = JSON.parse(raw);
726
+ } catch {
727
+ }
728
+ if (data?.settings_updated) {
729
+ finish(() => resolve(parseEffectiveSettings(data.settings)));
730
+ return;
731
+ }
732
+ if (data?.error) {
733
+ finish(() => reject(new KugelAudioError(String(data.error))));
734
+ return;
735
+ }
736
+ if (prevMessage) prevMessage.call(ws, event);
737
+ };
738
+ ws.onclose = (event) => {
739
+ if (prevClose) prevClose.call(ws, event);
740
+ finish(
741
+ () => reject(
742
+ new ConnectionError(
743
+ "WebSocket closed before settings_updated acknowledgement"
744
+ )
745
+ )
746
+ );
747
+ };
748
+ ws.send(JSON.stringify({ update_settings: body }));
749
+ });
750
+ }
626
751
  var _languageWarningLogged = false;
627
752
  function warnIfNoLanguage(language, normalize) {
628
753
  const normEnabled = normalize === void 0 || normalize;
@@ -678,6 +803,7 @@ var VoicesResource = class {
678
803
  category: v.category,
679
804
  sex: v.sex,
680
805
  age: v.age,
806
+ quality: v.quality,
681
807
  supportedLanguages: v.supported_languages || [],
682
808
  sampleText: v.sample_text,
683
809
  avatarUrl: v.avatar_url,
@@ -1137,7 +1263,7 @@ var TTSResource = class {
1137
1263
  text: options.text,
1138
1264
  model_id: options.modelId || "kugel-3",
1139
1265
  voice_id: options.voiceId,
1140
- cfg_scale: options.cfgScale ?? 2,
1266
+ cfg_scale: clampCfgScale(options.cfgScale) ?? 2,
1141
1267
  ...options.temperature !== void 0 && { temperature: options.temperature },
1142
1268
  max_new_tokens: options.maxNewTokens ?? 2048,
1143
1269
  sample_rate: options.sampleRate ?? 24e3,
@@ -1167,7 +1293,7 @@ var TTSResource = class {
1167
1293
  text: options.text,
1168
1294
  model_id: options.modelId || "kugel-3",
1169
1295
  voice_id: options.voiceId,
1170
- cfg_scale: options.cfgScale ?? 2,
1296
+ cfg_scale: clampCfgScale(options.cfgScale) ?? 2,
1171
1297
  max_new_tokens: options.maxNewTokens ?? 2048,
1172
1298
  sample_rate: options.sampleRate ?? 24e3,
1173
1299
  ...options.outputFormat && { output_format: options.outputFormat },
@@ -1522,7 +1648,7 @@ var MultiContextSession = class {
1522
1648
  warnIfNoLanguage(this.config.language, this.config.normalize);
1523
1649
  if (this.config.sampleRate) msg.sample_rate = this.config.sampleRate;
1524
1650
  if (this.config.outputFormat) msg.output_format = this.config.outputFormat;
1525
- if (this.config.cfgScale) msg.cfg_scale = this.config.cfgScale;
1651
+ if (this.config.cfgScale !== void 0) msg.cfg_scale = clampCfgScale(this.config.cfgScale);
1526
1652
  if (this.config.temperature !== void 0) msg.temperature = this.config.temperature;
1527
1653
  if (this.config.maxNewTokens) msg.max_new_tokens = this.config.maxNewTokens;
1528
1654
  if (this.config.normalize !== void 0) msg.normalize = this.config.normalize;
@@ -1599,6 +1725,33 @@ var MultiContextSession = class {
1599
1725
  context_id: contextId
1600
1726
  }));
1601
1727
  }
1728
+ /**
1729
+ * Change the session's generation parameters mid-connection (KUG-1166).
1730
+ *
1731
+ * Session-scoped (there is no `contextId`): the update applies to contexts
1732
+ * started **after** it — a context already streaming keeps the settings it
1733
+ * began with, since generation parameters are bound when a context's engine
1734
+ * session opens. With the common one-context-per-turn pattern that means it
1735
+ * takes effect on the next turn. Per-context `cfgScale` / `maxNewTokens`
1736
+ * passed to {@link createContext} still win for that context.
1737
+ *
1738
+ * Resolves with the generation parameters now in effect (the server's echo).
1739
+ *
1740
+ * @throws {KugelAudioError} if no field is provided, the socket is not open,
1741
+ * or the server rejects the update.
1742
+ */
1743
+ updateSettings(settings) {
1744
+ if (!this.ws || this.ws.readyState !== WS_OPEN) {
1745
+ throw new KugelAudioError(
1746
+ "MultiContextSession not connected. Call connect() first."
1747
+ );
1748
+ }
1749
+ const body = buildSettingsUpdateBody(settings);
1750
+ return sendUpdateSettings(this.ws, body).then((effective) => {
1751
+ applyAcceptedSettingsUpdate(this.config, settings);
1752
+ return effective;
1753
+ });
1754
+ }
1602
1755
  /**
1603
1756
  * Close the session and all contexts.
1604
1757
  */
@@ -1777,7 +1930,7 @@ var StreamingSession = class {
1777
1930
  if (!this.configSent) {
1778
1931
  if (this.config.voiceId !== void 0) msg.voice_id = this.config.voiceId;
1779
1932
  if (this.config.modelId !== void 0) msg.model_id = this.config.modelId;
1780
- if (this.config.cfgScale !== void 0) msg.cfg_scale = this.config.cfgScale;
1933
+ if (this.config.cfgScale !== void 0) msg.cfg_scale = clampCfgScale(this.config.cfgScale);
1781
1934
  if (this.config.temperature !== void 0) msg.temperature = this.config.temperature;
1782
1935
  if (this.config.maxNewTokens !== void 0) msg.max_new_tokens = this.config.maxNewTokens;
1783
1936
  if (this.config.sampleRate !== void 0) msg.sample_rate = this.config.sampleRate;
@@ -1926,6 +2079,39 @@ var StreamingSession = class {
1926
2079
  Object.assign(this.config, config);
1927
2080
  this.configSent = false;
1928
2081
  }
2082
+ /**
2083
+ * Change generation parameters mid-connection without reconnecting (KUG-1166).
2084
+ *
2085
+ * Sends an `update_settings` message and resolves with the generation
2086
+ * parameters now in effect (the server's echo). Only the six fields of
2087
+ * {@link SettingsUpdate} are updatable; identity / audio-format fields
2088
+ * (`voiceId`, `modelId`, `sampleRate`, `outputFormat`, `dictionaryIds`) are
2089
+ * fixed for the connection — change those with {@link updateConfig} after
2090
+ * {@link endSession} instead.
2091
+ *
2092
+ * The change applies to the **next turn**: a turn already streaming keeps the
2093
+ * settings it started with, so call this between turns.
2094
+ *
2095
+ * @example
2096
+ * ```typescript
2097
+ * const effective = await session.updateSettings({ cfgScale: 1.5, speed: 1.1 });
2098
+ * ```
2099
+ *
2100
+ * @throws {KugelAudioError} if no field is provided, the socket is not open,
2101
+ * or the server rejects the update (e.g. a value out of range).
2102
+ */
2103
+ updateSettings(settings) {
2104
+ if (!this.ws || this.ws.readyState !== WS_OPEN) {
2105
+ throw new KugelAudioError(
2106
+ "StreamingSession not connected. Call connect() first."
2107
+ );
2108
+ }
2109
+ const body = buildSettingsUpdateBody(settings);
2110
+ return sendUpdateSettings(this.ws, body).then((effective) => {
2111
+ applyAcceptedSettingsUpdate(this.config, settings);
2112
+ return effective;
2113
+ });
2114
+ }
1929
2115
  /**
1930
2116
  * Close the session and the WebSocket connection.
1931
2117
  *