@webex/internal-plugin-voicea 3.12.0-auth-prejoin-fetch.1 → 3.12.0-llmrefactor.10

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/voicea.js CHANGED
@@ -12,6 +12,7 @@ _Object$defineProperty(exports, "__esModule", {
12
12
  value: true
13
13
  });
14
14
  exports.default = exports.VoiceaChannel = void 0;
15
+ var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
15
16
  var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
16
17
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
17
18
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
@@ -20,6 +21,7 @@ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime
20
21
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/getPrototypeOf"));
21
22
  var _inherits2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/inherits"));
22
23
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
24
+ var _events = _interopRequireDefault(require("events"));
23
25
  var _uuid = _interopRequireDefault(require("uuid"));
24
26
  var _webexCore = require("@webex/webex-core");
25
27
  var _constants = require("./constants");
@@ -27,25 +29,28 @@ var _utils = require("./utils");
27
29
  function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
28
30
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
29
31
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? _Reflect$construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
30
- function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } // eslint-disable-next-line no-unused-vars
32
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } // @ts-ignore - webex-core types
33
+ // @ts-ignore - internal-plugin-llm types
31
34
  /**
32
- * @description VoiceaChannel to hold single instance of LLM
35
+ * @description VoiceaChannel handles voicea/transcription functionality for a single LLM connection.
36
+ * Created via `webex.internal.voicea.createChannel(llmChannel)`. The caller owns the
37
+ * channel and is responsible for its lifecycle.
33
38
  * @export
34
39
  * @class VoiceaChannel
35
40
  */
36
- var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
41
+ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_EventEmitter) {
37
42
  /**
38
- * Initializes Voicea plugin
39
- * @param {any} args
43
+ * Creates a VoiceaChannel, optionally bound to an LLMChannel.
44
+ * If no llmChannel is provided, call switchLLMChannel() later to attach one.
45
+ * @param {LLMChannel} [llmChannel] - The LLM channel to use (optional)
46
+ * @param {Function} request - The request function for making API calls (typically webex.request bound to webex)
40
47
  */
41
- function VoiceaChannel() {
48
+ function VoiceaChannel(llmChannel, request) {
42
49
  var _this;
43
50
  (0, _classCallCheck2.default)(this, VoiceaChannel);
44
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
45
- args[_key] = arguments[_key];
46
- }
47
- _this = _callSuper(this, VoiceaChannel, [].concat(args));
48
- (0, _defineProperty2.default)(_this, "namespace", _constants.VOICEA);
51
+ _this = _callSuper(this, VoiceaChannel);
52
+ (0, _defineProperty2.default)(_this, "request", void 0);
53
+ (0, _defineProperty2.default)(_this, "llmChannel", void 0);
49
54
  (0, _defineProperty2.default)(_this, "seqNum", void 0);
50
55
  (0, _defineProperty2.default)(_this, "areCaptionsEnabled", void 0);
51
56
  (0, _defineProperty2.default)(_this, "hasSubscribedToEvents", false);
@@ -57,9 +62,16 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
57
62
  (0, _defineProperty2.default)(_this, "currentSpokenLanguage", void 0);
58
63
  (0, _defineProperty2.default)(_this, "spokenLanguages", []);
59
64
  (0, _defineProperty2.default)(_this, "currentCaptionLanguage", void 0);
65
+ // Target channel for reconciler pattern - the channel voicea WANTS to be bound to
66
+ (0, _defineProperty2.default)(_this, "targetLLMChannel", void 0);
67
+ // Single pending 'online' listener for deferred reconciliation
68
+ (0, _defineProperty2.default)(_this, "_pendingOnlineListener", void 0);
69
+ // Tracks whether caption restoration is pending (set on switch, cleared on restore)
70
+ (0, _defineProperty2.default)(_this, "_pendingCaptionRestore", false);
60
71
  /**
61
- * @param {Object} e
62
- * @returns {undefined}
72
+ * Process events from LLM channel
73
+ * @param {Object} e - Event data
74
+ * @returns {void}
63
75
  */
64
76
  (0, _defineProperty2.default)(_this, "eventProcessor", function (e) {
65
77
  var _e$headers;
@@ -76,6 +88,9 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
76
88
  case _constants.AIBRIDGE_RELAY_TYPES.VOICEA.TRANSCRIPTION:
77
89
  _this.processTranscription(e.data.voiceaPayload);
78
90
  break;
91
+ case _constants.AIBRIDGE_RELAY_TYPES.VOICEA.SPEAKER_NAME_UPDATE:
92
+ _this.processSpeakerNameUpdate(e.data.voiceaPayload);
93
+ break;
79
94
  case _constants.AIBRIDGE_RELAY_TYPES.MANUAL.TRANSCRIPTION:
80
95
  case _constants.AIBRIDGE_RELAY_TYPES.MANUAL.CAPTIONER:
81
96
  _this.processManualTranscription(_objectSpread(_objectSpread({}, e.data.transcriptPayload), {}, {
@@ -94,8 +109,7 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
94
109
  */
95
110
  (0, _defineProperty2.default)(_this, "processManualTranscription", function (transcriptPayload) {
96
111
  if (transcriptPayload.type === _constants.TRANSCRIPTION_TYPE.MANUAL_CAPTION_FINAL_RESULT || transcriptPayload.type === _constants.TRANSCRIPTION_TYPE.MANUAL_CAPTION_INTERIM_RESULT) {
97
- // @ts-ignore
98
- _this.trigger(_constants.EVENT_TRIGGERS.NEW_MANUAL_CAPTION, {
112
+ _this.emit(_constants.EVENT_TRIGGERS.NEW_MANUAL_CAPTION, {
99
113
  isFinal: transcriptPayload.type === _constants.TRANSCRIPTION_TYPE.MANUAL_CAPTION_FINAL_RESULT,
100
114
  transcriptId: transcriptPayload.id,
101
115
  transcripts: transcriptPayload.transcripts,
@@ -104,54 +118,59 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
104
118
  });
105
119
  }
106
120
  });
121
+ /**
122
+ * Process speaker name update and send alert
123
+ * @param {SpeakerNameUpdatePayload} voiceaPayload
124
+ * @returns {void}
125
+ */
126
+ (0, _defineProperty2.default)(_this, "processSpeakerNameUpdate", function (voiceaPayload) {
127
+ // @ts-ignore
128
+ _this.emit(_constants.EVENT_TRIGGERS.SPEAKER_NAME_UPDATED, voiceaPayload);
129
+ });
107
130
  /**
108
131
  * Process Transcript and send alert
109
132
  * @param {TranscriptionResponse} voiceaPayload
110
133
  * @returns {void}
111
134
  */
112
135
  (0, _defineProperty2.default)(_this, "processTranscription", function (voiceaPayload) {
136
+ var _voiceaPayload$transc, _voiceaPayload$highli, _voiceaPayload$highli2, _voiceaPayload$highli3, _voiceaPayload$highli4, _voiceaPayload$highli5, _voiceaPayload$highli6, _voiceaPayload$highli7;
113
137
  switch (voiceaPayload.type) {
114
138
  case _constants.TRANSCRIPTION_TYPE.TRANSCRIPT_INTERIM_RESULTS:
115
- // @ts-ignore
116
- _this.trigger(_constants.EVENT_TRIGGERS.NEW_CAPTION, {
139
+ _this.emit(_constants.EVENT_TRIGGERS.NEW_CAPTION, {
117
140
  isFinal: false,
118
141
  transcriptId: voiceaPayload.transcript_id,
119
142
  transcripts: voiceaPayload.transcripts
120
143
  });
121
144
  break;
122
145
  case _constants.TRANSCRIPTION_TYPE.TRANSCRIPT_FINAL_RESULT:
123
- // @ts-ignore
124
- _this.trigger(_constants.EVENT_TRIGGERS.NEW_CAPTION, {
146
+ _this.emit(_constants.EVENT_TRIGGERS.NEW_CAPTION, {
125
147
  isFinal: true,
126
148
  transcriptId: voiceaPayload.transcript_id,
127
- transcripts: voiceaPayload.transcripts.map(function (transcript) {
149
+ transcripts: (_voiceaPayload$transc = voiceaPayload.transcripts) === null || _voiceaPayload$transc === void 0 ? void 0 : _voiceaPayload$transc.map(function (transcript) {
128
150
  transcript.timestamp = (0, _utils.millisToMinutesAndSeconds)(transcript.end_millis);
129
151
  return transcript;
130
152
  })
131
153
  });
132
154
  break;
133
155
  case _constants.TRANSCRIPTION_TYPE.HIGHLIGHT_CREATED:
134
- // @ts-ignore
135
- _this.trigger(_constants.EVENT_TRIGGERS.HIGHLIGHT_CREATED, {
136
- csis: voiceaPayload.highlight.csis,
137
- highlightId: voiceaPayload.highlight.highlight_id,
138
- text: voiceaPayload.highlight.transcript,
139
- highlightLabel: voiceaPayload.highlight.highlight_label,
140
- highlightSource: voiceaPayload.highlight.highlight_source,
141
- timestamp: (0, _utils.millisToMinutesAndSeconds)(voiceaPayload.highlight.end_millis)
156
+ _this.emit(_constants.EVENT_TRIGGERS.HIGHLIGHT_CREATED, {
157
+ csis: (_voiceaPayload$highli = voiceaPayload.highlight) === null || _voiceaPayload$highli === void 0 ? void 0 : _voiceaPayload$highli.csis,
158
+ highlightId: (_voiceaPayload$highli2 = voiceaPayload.highlight) === null || _voiceaPayload$highli2 === void 0 ? void 0 : _voiceaPayload$highli2.highlight_id,
159
+ text: (_voiceaPayload$highli3 = voiceaPayload.highlight) === null || _voiceaPayload$highli3 === void 0 ? void 0 : _voiceaPayload$highli3.transcript,
160
+ highlightLabel: (_voiceaPayload$highli4 = voiceaPayload.highlight) === null || _voiceaPayload$highli4 === void 0 ? void 0 : _voiceaPayload$highli4.highlight_label,
161
+ highlightSource: (_voiceaPayload$highli5 = voiceaPayload.highlight) === null || _voiceaPayload$highli5 === void 0 ? void 0 : _voiceaPayload$highli5.highlight_source,
162
+ timestamp: (0, _utils.millisToMinutesAndSeconds)((_voiceaPayload$highli6 = (_voiceaPayload$highli7 = voiceaPayload.highlight) === null || _voiceaPayload$highli7 === void 0 ? void 0 : _voiceaPayload$highli7.end_millis) !== null && _voiceaPayload$highli6 !== void 0 ? _voiceaPayload$highli6 : 0)
142
163
  });
143
164
  break;
144
165
  case _constants.TRANSCRIPTION_TYPE.EVA_THANKS:
145
- // @ts-ignore
146
- _this.trigger(_constants.EVENT_TRIGGERS.EVA_COMMAND, {
166
+ _this.emit(_constants.EVENT_TRIGGERS.EVA_COMMAND, {
147
167
  isListening: false,
148
168
  text: voiceaPayload.command_response
149
169
  });
150
170
  break;
151
171
  case _constants.TRANSCRIPTION_TYPE.EVA_WAKE:
152
172
  case _constants.TRANSCRIPTION_TYPE.EVA_CANCEL:
153
- // @ts-ignore
154
- _this.trigger(_constants.EVENT_TRIGGERS.EVA_COMMAND, {
173
+ _this.emit(_constants.EVENT_TRIGGERS.EVA_COMMAND, {
155
174
  isListening: voiceaPayload.type === _constants.TRANSCRIPTION_TYPE.EVA_WAKE
156
175
  });
157
176
  break;
@@ -159,8 +178,7 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
159
178
  {
160
179
  var isInSpokenLanguages = _this.spokenLanguages.includes(voiceaPayload.language);
161
180
  if (isInSpokenLanguages) {
162
- // @ts-ignore
163
- _this.trigger(_constants.EVENT_TRIGGERS.LANGUAGE_DETECTED, {
181
+ _this.emit(_constants.EVENT_TRIGGERS.LANGUAGE_DETECTED, {
164
182
  languageCode: voiceaPayload.language
165
183
  });
166
184
  }
@@ -177,13 +195,11 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
177
195
  */
178
196
  (0, _defineProperty2.default)(_this, "processCaptionLanguageResponse", function (voiceaPayload) {
179
197
  if (voiceaPayload.statusCode === 200) {
180
- // @ts-ignore
181
- _this.trigger(_constants.EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE, {
198
+ _this.emit(_constants.EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE, {
182
199
  statusCode: 200
183
200
  });
184
201
  } else {
185
- // @ts-ignore
186
- _this.trigger(_constants.EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE, {
202
+ _this.emit(_constants.EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE, {
187
203
  statusCode: voiceaPayload.errorCode,
188
204
  errorMessage: voiceaPayload.message
189
205
  });
@@ -203,50 +219,29 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
203
219
  currentSpokenLanguage: _this.currentSpokenLanguage
204
220
  };
205
221
  _this.spokenLanguages = (_voiceaPayload$ASR$sp2 = voiceaPayload === null || voiceaPayload === void 0 ? void 0 : (_voiceaPayload$ASR2 = voiceaPayload.ASR) === null || _voiceaPayload$ASR2 === void 0 ? void 0 : _voiceaPayload$ASR2.spoken_languages) !== null && _voiceaPayload$ASR$sp2 !== void 0 ? _voiceaPayload$ASR$sp2 : [];
206
- // @ts-ignore
207
- _this.trigger(_constants.EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, voiceaLanguageOptions);
222
+ _this.emit(_constants.EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, voiceaLanguageOptions);
208
223
  });
209
224
  /**
210
- * Indicates whether the default or practice-session LLM connection is active.
225
+ * Indicates whether the LLM channel is connected.
211
226
  * @returns {boolean}
212
227
  */
213
228
  (0, _defineProperty2.default)(_this, "isLLMConnected", function () {
214
- return (
215
- // @ts-ignore
216
- _this.webex.internal.llm.isConnected() ||
217
- // @ts-ignore
218
- _this.webex.internal.llm.isConnected(_constants.LLM_PRACTICE_SESSION)
219
- );
229
+ var _this$llmChannel$isCo, _this$llmChannel;
230
+ return (_this$llmChannel$isCo = (_this$llmChannel = _this.llmChannel) === null || _this$llmChannel === void 0 ? void 0 : _this$llmChannel.isConnected()) !== null && _this$llmChannel$isCo !== void 0 ? _this$llmChannel$isCo : false;
220
231
  });
221
232
  (0, _defineProperty2.default)(_this, "getKeepTranscriptionSubscribed", function () {
222
233
  return _this.keepTranscriptionSubscribed;
223
234
  });
224
- /**
225
- * Resolves the active LLM publish transport, preferring the practice-session
226
- * connection only when that session is fully connected.
227
- * @returns {Object}
228
- */
229
- (0, _defineProperty2.default)(_this, "getPublishTransport", function () {
230
- // @ts-ignore
231
- var llm = _this.webex.internal.llm;
232
- var isPracticeSessionConnected = llm.isConnected(_constants.LLM_PRACTICE_SESSION);
233
- return {
234
- socket: isPracticeSessionConnected && llm.getSocket(_constants.LLM_PRACTICE_SESSION) || llm.socket,
235
- binding: isPracticeSessionConnected && llm.getBinding(_constants.LLM_PRACTICE_SESSION) || llm.getBinding(),
236
- datachannelUrl: isPracticeSessionConnected && llm.getDatachannelUrl(_constants.LLM_PRACTICE_SESSION) || llm.getDatachannelUrl()
237
- };
238
- });
239
235
  /**
240
236
  * Sends Announcement to add voicea to the meeting
241
237
  * @returns {void}
242
238
  */
243
239
  (0, _defineProperty2.default)(_this, "sendAnnouncement", function () {
240
+ var llm = _this.requireLLMChannel();
244
241
  _this.announceStatus = _constants.ANNOUNCE_STATUS.JOINING;
245
- _this.listenToEvents();
246
- var _this$getPublishTrans = _this.getPublishTransport(),
247
- socket = _this$getPublishTrans.socket,
248
- binding = _this$getPublishTrans.binding;
249
- socket.send({
242
+ var socket = llm.getSocket();
243
+ var binding = llm.getBinding();
244
+ var payload = {
250
245
  id: "".concat(_this.seqNum),
251
246
  type: 'publishRequest',
252
247
  recipients: [{
@@ -264,7 +259,8 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
264
259
  relayType: _constants.AIBRIDGE_RELAY_TYPES.VOICEA.CLIENT_ANNOUNCEMENT
265
260
  },
266
261
  trackingId: "".concat(_webexCore.config.trackingIdPrefix, "_").concat(_uuid.default.v4().toString())
267
- });
262
+ };
263
+ socket.send(payload);
268
264
  _this.seqNum += 1;
269
265
  });
270
266
  /**
@@ -274,26 +270,21 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
274
270
  * @returns {Promise}
275
271
  */
276
272
  (0, _defineProperty2.default)(_this, "setSpokenLanguage", function (languageCode, languageAssignment) {
277
- return (
278
- // @ts-ignore
279
- _this.request({
280
- method: 'PUT',
281
- // @ts-ignore
282
- url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
283
- body: {
284
- transcribe: _objectSpread({
285
- spokenLanguage: languageCode
286
- }, languageAssignment && {
287
- languageAssignment: languageAssignment
288
- })
289
- }
290
- }).then(function () {
291
- // @ts-ignore
292
- _this.trigger(_constants.EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, {
293
- languageCode: languageCode
294
- });
295
- })
296
- );
273
+ return _this.request({
274
+ method: 'PUT',
275
+ url: "".concat(_this.requireLLMChannel().getLocusUrl(), "/controls/"),
276
+ body: {
277
+ transcribe: _objectSpread({
278
+ spokenLanguage: languageCode
279
+ }, languageAssignment && {
280
+ languageAssignment: languageAssignment
281
+ })
282
+ }
283
+ }).then(function () {
284
+ _this.emit(_constants.EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, {
285
+ languageCode: languageCode
286
+ });
287
+ });
297
288
  });
298
289
  /**
299
290
  * Request Language translation
@@ -304,9 +295,9 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
304
295
  if (!_this.isLLMConnected()) {
305
296
  return;
306
297
  }
307
- var _this$getPublishTrans2 = _this.getPublishTransport(),
308
- socket = _this$getPublishTrans2.socket,
309
- binding = _this$getPublishTrans2.binding;
298
+ var llm = _this.requireLLMChannel();
299
+ var socket = llm.getSocket();
300
+ var binding = llm.getBinding();
310
301
  socket.send({
311
302
  id: "".concat(_this.seqNum),
312
303
  type: 'publishRequest',
@@ -341,9 +332,9 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
341
332
  if (!_this.isLLMConnected()) {
342
333
  return;
343
334
  }
344
- var _this$getPublishTrans3 = _this.getPublishTransport(),
345
- socket = _this$getPublishTrans3.socket,
346
- binding = _this$getPublishTrans3.binding;
335
+ var llm = _this.requireLLMChannel();
336
+ var socket = llm.getSocket();
337
+ var binding = llm.getBinding();
347
338
  socket === null || socket === void 0 ? void 0 : socket.send({
348
339
  id: "".concat(_this.seqNum),
349
340
  type: 'publishRequest',
@@ -372,39 +363,35 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
372
363
  });
373
364
  /**
374
365
  * request turn on Captions
375
- * @param {string} [languageCode] - Optional Parameter for spoken language code. Defaults to English
366
+ * @param {string} [languageCode] - Optional Parameter for spoken language code
376
367
  * @returns {Promise}
377
368
  */
378
369
  (0, _defineProperty2.default)(_this, "requestTurnOnCaptions", function (languageCode) {
379
370
  _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.SENDING;
380
-
381
- // only set the spoken language if it is provided
371
+ var locusUrl = _this.requireLLMChannel().getLocusUrl();
382
372
  var body = {
383
373
  transcribe: {
384
374
  caption: true
385
375
  },
386
376
  languageCode: languageCode
387
377
  };
388
-
389
- // @ts-ignore
390
- // eslint-disable-next-line newline-before-return
391
378
  return _this.request({
392
379
  method: 'PUT',
393
- // @ts-ignore
394
- url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
380
+ url: "".concat(locusUrl, "/controls/"),
395
381
  body: body
396
382
  }).then(function () {
397
- // @ts-ignore
398
- _this.trigger(_constants.EVENT_TRIGGERS.CAPTIONS_TURNED_ON);
383
+ _this.emit(_constants.EVENT_TRIGGERS.CAPTIONS_TURNED_ON);
399
384
  _this.areCaptionsEnabled = true;
400
385
  _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.ENABLED;
401
386
  _this.announce();
402
387
  _this.updateSubchannelSubscriptionsAndSyncCaptionState({
403
388
  subscribe: ['transcription']
404
389
  }, true);
405
- }).catch(function () {
390
+ }).catch(function (error) {
406
391
  _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
407
- throw new Error('turn on captions fail');
392
+ throw new Error('turn on captions fail', {
393
+ cause: error
394
+ });
408
395
  });
409
396
  });
410
397
  /**
@@ -422,7 +409,7 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
422
409
  return _this.announceStatus === _constants.ANNOUNCE_STATUS.JOINED;
423
410
  });
424
411
  /**
425
- * announce to voicea data chanel
412
+ * announce to voicea data channel
426
413
  * @returns {void}
427
414
  */
428
415
  (0, _defineProperty2.default)(_this, "announce", function () {
@@ -451,7 +438,7 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
451
438
  return _regenerator.default.wrap(function (_context) {
452
439
  while (1) switch (_context.prev = _context.next) {
453
440
  case 0:
454
- if (!(_this.captionStatus === _constants.TURN_ON_CAPTION_STATUS.SENDING)) {
441
+ if (!_this.isCaptionProcessing()) {
455
442
  _context.next = 1;
456
443
  break;
457
444
  }
@@ -481,11 +468,9 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
481
468
  * @returns {Promise}
482
469
  */
483
470
  (0, _defineProperty2.default)(_this, "toggleTranscribing", function (activate, spokenLanguage) {
484
- // @ts-ignore
485
471
  return _this.request({
486
472
  method: 'PUT',
487
- // @ts-ignore
488
- url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
473
+ url: "".concat(_this.requireLLMChannel().getLocusUrl(), "/controls/"),
489
474
  body: {
490
475
  transcribe: {
491
476
  transcribing: activate
@@ -507,12 +492,9 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
507
492
  (0, _defineProperty2.default)(_this, "toggleManualCaption", function (enable) {
508
493
  if (_this.toggleManualCaptionStatus === _constants.TOGGLE_MANUAL_CAPTION_STATUS.SENDING) return undefined;
509
494
  _this.toggleManualCaptionStatus = _constants.TOGGLE_MANUAL_CAPTION_STATUS.SENDING;
510
-
511
- // @ts-ignore
512
495
  return _this.request({
513
496
  method: 'PUT',
514
- // @ts-ignore
515
- url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
497
+ url: "".concat(_this.requireLLMChannel().getLocusUrl(), "/controls/"),
516
498
  body: {
517
499
  manualCaption: {
518
500
  enable: enable
@@ -533,8 +515,7 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
533
515
  * @returns {void}
534
516
  */
535
517
  (0, _defineProperty2.default)(_this, "onSpokenLanguageUpdate", function (languageCode, meetingId) {
536
- // @ts-ignore
537
- _this.trigger(_constants.EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, {
518
+ _this.emit(_constants.EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, {
538
519
  languageCode: languageCode,
539
520
  meetingId: meetingId
540
521
  });
@@ -551,7 +532,6 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
551
532
  }
552
533
  if (_this.captionServiceId !== serviceId) {
553
534
  _this.captionServiceId = serviceId;
554
- // if service id value has changed and the translation language has been set, client needs to resend the translator language message to the LLM.
555
535
  if (_this.currentCaptionLanguage) {
556
536
  _this.requestLanguage(_this.currentCaptionLanguage);
557
537
  }
@@ -574,9 +554,6 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
574
554
  /**
575
555
  * update LLM sub‑channel subscriptions.
576
556
  *
577
- * sends a single `subchannelSubscriptionRequest` to LLM,
578
- * allowing subscribe and unsubscribe subchannel.
579
- *
580
557
  * @param {string[]} options.subscribe Sub‑channels to subscribe to.
581
558
  * @param {string[]} options.unsubscribe Sub‑channels to unsubscribe from.
582
559
  * @returns {Promise}
@@ -587,8 +564,8 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
587
564
  subscribe,
588
565
  _ref3$unsubscribe,
589
566
  unsubscribe,
567
+ llm,
590
568
  isDataChannelTokenEnabled,
591
- _this$getPublishTrans4,
592
569
  socket,
593
570
  datachannelUrl,
594
571
  _args2 = arguments;
@@ -596,22 +573,29 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
596
573
  while (1) switch (_context2.prev = _context2.next) {
597
574
  case 0:
598
575
  _ref3 = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : {}, _ref3$subscribe = _ref3.subscribe, subscribe = _ref3$subscribe === void 0 ? [] : _ref3$subscribe, _ref3$unsubscribe = _ref3.unsubscribe, unsubscribe = _ref3$unsubscribe === void 0 ? [] : _ref3$unsubscribe;
599
- _context2.next = 1;
600
- return _this.webex.internal.llm.isDataChannelTokenEnabled();
576
+ if (_this.isLLMConnected()) {
577
+ _context2.next = 1;
578
+ break;
579
+ }
580
+ return _context2.abrupt("return");
601
581
  case 1:
582
+ llm = _this.requireLLMChannel();
583
+ _context2.next = 2;
584
+ return llm.isDataChannelTokenEnabled();
585
+ case 2:
602
586
  isDataChannelTokenEnabled = _context2.sent;
603
- if (!(!_this.isLLMConnected() || !isDataChannelTokenEnabled)) {
604
- _context2.next = 2;
587
+ if (isDataChannelTokenEnabled) {
588
+ _context2.next = 3;
605
589
  break;
606
590
  }
607
591
  return _context2.abrupt("return");
608
- case 2:
609
- _this$getPublishTrans4 = _this.getPublishTransport(), socket = _this$getPublishTrans4.socket, datachannelUrl = _this$getPublishTrans4.datachannelUrl; // @ts-ignore
592
+ case 3:
593
+ socket = llm.getSocket();
594
+ datachannelUrl = llm.getDatachannelUrl();
610
595
  socket.send({
611
596
  id: "".concat(_this.seqNum),
612
597
  type: 'subchannelSubscriptionRequest',
613
598
  data: {
614
- // @ts-ignore
615
599
  datachannelUri: datachannelUrl,
616
600
  subscribe: subscribe,
617
601
  unsubscribe: unsubscribe
@@ -619,7 +603,7 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
619
603
  trackingId: "".concat(_webexCore.config.trackingIdPrefix, "_").concat(_uuid.default.v4().toString())
620
604
  });
621
605
  _this.seqNum += 1;
622
- case 3:
606
+ case 4:
623
607
  case "end":
624
608
  return _context2.stop();
625
609
  }
@@ -643,57 +627,174 @@ var VoiceaChannel = exports.VoiceaChannel = /*#__PURE__*/function (_WebexPlugin)
643
627
  _this.keepTranscriptionSubscribed = keepSubscribed;
644
628
  return _this.updateSubchannelSubscriptions(options);
645
629
  });
630
+ _this.llmChannel = llmChannel;
631
+ _this.request = request;
646
632
  _this.seqNum = 1;
647
633
  _this.areCaptionsEnabled = false;
648
- _this.keepTranscriptionSubscribed = false;
649
634
  _this.captionServiceId = undefined;
650
635
  _this.announceStatus = _constants.ANNOUNCE_STATUS.IDLE;
651
636
  _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
652
637
  _this.toggleManualCaptionStatus = _constants.TOGGLE_MANUAL_CAPTION_STATUS.IDLE;
653
638
  _this.currentSpokenLanguage = _constants.DEFAULT_SPOKEN_LANGUAGE;
654
639
  _this.currentCaptionLanguage = undefined;
640
+ _this.keepTranscriptionSubscribed = false;
641
+
642
+ // Subscribe to relay events from the LLM channel if provided
643
+ if (_this.llmChannel) {
644
+ _this.llmChannel.on('event:relay.event', _this.eventProcessor);
645
+ _this.hasSubscribedToEvents = true;
646
+ }
655
647
  return _this;
656
648
  }
657
- (0, _inherits2.default)(VoiceaChannel, _WebexPlugin);
649
+
650
+ /**
651
+ * Returns the LLM channel, throwing if not available.
652
+ * @private
653
+ * @returns {LLMChannel}
654
+ * @throws {Error} If LLM channel is not available
655
+ */
656
+ (0, _inherits2.default)(VoiceaChannel, _EventEmitter);
658
657
  return (0, _createClass2.default)(VoiceaChannel, [{
659
- key: "listenToEvents",
660
- value:
661
- /**
662
- * Listen to websocket messages
663
- * @returns {undefined}
664
- */
665
- function listenToEvents() {
666
- if (!this.hasSubscribedToEvents) {
667
- // @ts-ignore
668
- this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
669
- // @ts-ignore
670
- this.webex.internal.llm.on("event:relay.event:".concat(_constants.LLM_PRACTICE_SESSION), this.eventProcessor);
671
- this.hasSubscribedToEvents = true;
658
+ key: "requireLLMChannel",
659
+ value: function requireLLMChannel() {
660
+ if (!this.llmChannel) {
661
+ throw new Error('VoiceaChannel: LLM channel not available');
672
662
  }
663
+ return this.llmChannel;
673
664
  }
674
-
665
+ }, {
666
+ key: "deregisterEvents",
667
+ value:
675
668
  /**
676
- * Listen to websocket messages
669
+ * Deregister events and clean up
677
670
  * @returns {void}
678
671
  */
679
- }, {
680
- key: "deregisterEvents",
681
- value: function deregisterEvents() {
672
+ function deregisterEvents() {
682
673
  this.areCaptionsEnabled = false;
683
674
  this.keepTranscriptionSubscribed = false;
684
675
  this.captionServiceId = undefined;
685
- // @ts-ignore
686
- this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
687
- // @ts-ignore
688
- this.webex.internal.llm.off("event:relay.event:".concat(_constants.LLM_PRACTICE_SESSION), this.eventProcessor);
689
- this.hasSubscribedToEvents = false;
676
+ this.targetLLMChannel = undefined;
677
+ this._pendingCaptionRestore = false;
678
+
679
+ // Remove any pending online listener
680
+ this.detachPendingOnline();
681
+ if (this.hasSubscribedToEvents) {
682
+ var _this$llmChannel2;
683
+ (_this$llmChannel2 = this.llmChannel) === null || _this$llmChannel2 === void 0 ? void 0 : _this$llmChannel2.off('event:relay.event', this.eventProcessor);
684
+ this.hasSubscribedToEvents = false;
685
+ }
690
686
  this.announceStatus = _constants.ANNOUNCE_STATUS.IDLE;
691
687
  this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
692
688
  this.toggleManualCaptionStatus = _constants.TOGGLE_MANUAL_CAPTION_STATUS.IDLE;
693
689
  this.currentSpokenLanguage = undefined;
694
690
  this.currentCaptionLanguage = undefined;
695
691
  }
692
+
693
+ /**
694
+ * Attach a pending 'online' listener to a channel for deferred reconciliation.
695
+ * Detaches any existing listener first.
696
+ * @param {LLMChannel} channel - The channel to attach to
697
+ * @param {Function} callback - The callback to run when 'online' fires
698
+ * @private
699
+ * @returns {void}
700
+ */
701
+ }, {
702
+ key: "attachPendingOnline",
703
+ value: function attachPendingOnline(channel, callback) {
704
+ this.detachPendingOnline();
705
+ this._pendingOnlineListener = callback;
706
+ channel.once('online', this._pendingOnlineListener);
707
+ }
708
+
709
+ /**
710
+ * Detach any pending 'online' listener.
711
+ * @private
712
+ * @returns {void}
713
+ */
714
+ }, {
715
+ key: "detachPendingOnline",
716
+ value: function detachPendingOnline() {
717
+ if (this._pendingOnlineListener && this.llmChannel) {
718
+ this.llmChannel.off('online', this._pendingOnlineListener);
719
+ }
720
+ this._pendingOnlineListener = undefined;
721
+ }
722
+
723
+ /**
724
+ * Reset announcement state for a new connection.
725
+ * @private
726
+ * @returns {void}
727
+ */
728
+ }, {
729
+ key: "resetAnnounceState",
730
+ value: function resetAnnounceState() {
731
+ this.announceStatus = _constants.ANNOUNCE_STATUS.IDLE;
732
+ this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
733
+ this.captionServiceId = undefined;
734
+ this.areCaptionsEnabled = false;
735
+ }
736
+
737
+ /**
738
+ * Reconcile voicea binding and caption state to match the desired target.
739
+ * This is idempotent - always re-reads targetLLMChannel and current state.
740
+ * A deferred 'online' callback that fires late self-corrects automatically.
741
+ * @private
742
+ * @returns {Promise<void>}
743
+ */
744
+ }, {
745
+ key: "reconcile",
746
+ value: function reconcile() {
747
+ var _this2 = this;
748
+ var target = this.targetLLMChannel;
749
+ if (!target) return _promise.default.resolve();
750
+
751
+ // 1. Rebind relay-event subscription if actual != desired
752
+ if (this.llmChannel !== target) {
753
+ if (this.hasSubscribedToEvents && this.llmChannel) {
754
+ this.llmChannel.off('event:relay.event', this.eventProcessor);
755
+ }
756
+ this.detachPendingOnline();
757
+ this.llmChannel = target;
758
+ this.llmChannel.on('event:relay.event', this.eventProcessor);
759
+ this.hasSubscribedToEvents = true;
760
+ this.resetAnnounceState();
761
+ // Mark caption restoration as pending if captions were subscribed
762
+ this._pendingCaptionRestore = this.keepTranscriptionSubscribed;
763
+ }
764
+
765
+ // 2. Restore captions if pending
766
+ if (!this._pendingCaptionRestore) return _promise.default.resolve();
767
+ if (this.isLLMConnected()) {
768
+ // Channel is connected, restore captions immediately
769
+ this._pendingCaptionRestore = false;
770
+ return this.turnOnCaptions(this.currentSpokenLanguage).then(function () {
771
+ return undefined;
772
+ }).catch(function () {
773
+ // Best-effort restoration
774
+ });
775
+ }
776
+ // Channel not yet connected - defer until 'online' then re-reconcile.
777
+ // If target changes meanwhile, reconcile() self-corrects.
778
+ this.attachPendingOnline(target, function () {
779
+ _this2.reconcile();
780
+ });
781
+ return _promise.default.resolve();
782
+ }
783
+
784
+ /**
785
+ * Switch to a different LLM channel while preserving caption state.
786
+ * Used when transitioning between main meeting and practice session.
787
+ * This is a thin intent-setter - actual binding happens in reconcile().
788
+ * @param {LLMChannel} newLLMChannel - The new LLM channel to switch to
789
+ * @returns {Promise<void>}
790
+ */
791
+ }, {
792
+ key: "switchLLMChannel",
793
+ value: function switchLLMChannel(newLLMChannel) {
794
+ this.targetLLMChannel = newLLMChannel;
795
+ return this.reconcile();
796
+ }
696
797
  }]);
697
- }(_webexCore.WebexPlugin);
798
+ }(_events.default);
698
799
  var _default = exports.default = VoiceaChannel;
699
800
  //# sourceMappingURL=voicea.js.map