@webex/internal-plugin-llm 3.12.0-next.37 → 3.12.0-next.39

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":["WebexCore","_interopRequireWildcard","require","_llm","_llm2","_constants","e","t","_WeakMap","r","n","__esModule","o","i","f","__proto__","default","_typeof","has","get","set","_t","hasOwnProperty","call","_Object$defineProperty","_Object$getOwnPropertyDescriptor","registerInternalPlugin","LLMChannel","config"],"sources":["index.ts"],"sourcesContent":["import * as WebexCore from '@webex/webex-core';\nimport LLMChannel, {config} from './llm';\nimport {DataChannelTokenType} from './llm.types';\n\nWebexCore.registerInternalPlugin('llm', LLMChannel, {\n config,\n});\n\nexport {DataChannelTokenType};\nexport {LLM_DEFAULT_SESSION, LLM_PRACTICE_SESSION} from './constants';\nexport {default} from './llm';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,SAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,IAAA,GAAAF,uBAAA,CAAAC,OAAA;AACA,IAAAE,KAAA,GAAAF,OAAA;AAOA,IAAAG,UAAA,GAAAH,OAAA;AAAsE,SAAAD,wBAAAK,CAAA,EAAAC,CAAA,6BAAAC,QAAA,MAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAP,uBAAA,YAAAA,wBAAAK,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,gBAAAW,OAAA,CAAAX,CAAA,0BAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAM,GAAA,CAAAZ,CAAA,UAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,GAAAM,CAAA,CAAAQ,GAAA,CAAAd,CAAA,EAAAQ,CAAA,cAAAO,EAAA,IAAAf,CAAA,gBAAAe,EAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAjB,CAAA,EAAAe,EAAA,OAAAR,CAAA,IAAAD,CAAA,GAAAY,sBAAA,KAAAC,gCAAA,CAAAnB,CAAA,EAAAe,EAAA,OAAAR,CAAA,CAAAM,GAAA,IAAAN,CAAA,CAAAO,GAAA,IAAAR,CAAA,CAAAE,CAAA,EAAAO,EAAA,EAAAR,CAAA,IAAAC,CAAA,CAAAO,EAAA,IAAAf,CAAA,CAAAe,EAAA,WAAAP,CAAA,KAAAR,CAAA,EAAAC,CAAA;AALtEP,SAAS,CAAC0B,sBAAsB,CAAC,KAAK,EAAEC,YAAU,EAAE;EAClDC,MAAM,EAANA;AACF,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["WebexCore","_interopRequireWildcard","require","_llm","_llm2","_constants","e","t","_WeakMap","r","n","__esModule","o","i","f","__proto__","default","_typeof","has","get","set","_t","hasOwnProperty","call","_Object$defineProperty","_Object$getOwnPropertyDescriptor","registerInternalPlugin","LLMChannel","config"],"sources":["index.ts"],"sourcesContent":["import * as WebexCore from '@webex/webex-core';\nimport LLMChannel, {config} from './llm';\nimport {DataChannelTokenType} from './llm.types';\n\nexport type {RegisterAndConnectTiming} from './llm.types';\n\nWebexCore.registerInternalPlugin('llm', LLMChannel, {\n config,\n});\n\nexport {DataChannelTokenType};\nexport {LLM_DEFAULT_SESSION, LLM_PRACTICE_SESSION} from './constants';\nexport {default} from './llm';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,SAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,IAAA,GAAAF,uBAAA,CAAAC,OAAA;AACA,IAAAE,KAAA,GAAAF,OAAA;AASA,IAAAG,UAAA,GAAAH,OAAA;AAAsE,SAAAD,wBAAAK,CAAA,EAAAC,CAAA,6BAAAC,QAAA,MAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAP,uBAAA,YAAAA,wBAAAK,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,gBAAAW,OAAA,CAAAX,CAAA,0BAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAM,GAAA,CAAAZ,CAAA,UAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,GAAAM,CAAA,CAAAQ,GAAA,CAAAd,CAAA,EAAAQ,CAAA,cAAAO,EAAA,IAAAf,CAAA,gBAAAe,EAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAjB,CAAA,EAAAe,EAAA,OAAAR,CAAA,IAAAD,CAAA,GAAAY,sBAAA,KAAAC,gCAAA,CAAAnB,CAAA,EAAAe,EAAA,OAAAR,CAAA,CAAAM,GAAA,IAAAN,CAAA,CAAAO,GAAA,IAAAR,CAAA,CAAAE,CAAA,EAAAO,EAAA,EAAAR,CAAA,IAAAC,CAAA,CAAAO,EAAA,IAAAf,CAAA,CAAAe,EAAA,WAAAP,CAAA,KAAAR,CAAA,EAAAC,CAAA;AALtEP,SAAS,CAAC0B,sBAAsB,CAAC,KAAK,EAAEC,YAAU,EAAE;EAClDC,MAAM,EAANA;AACF,CAAC,CAAC","ignoreList":[]}
package/dist/llm.js CHANGED
@@ -143,6 +143,8 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
143
143
  */
144
144
  (0, _defineProperty3.default)(_this, "registerAndConnect", function (locusUrl, datachannelUrl, datachannelToken) {
145
145
  var sessionId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _constants.LLM_DEFAULT_SESSION;
146
+ var registerStart = performance.now();
147
+
146
148
  // Pre-populate locusUrl and datachannelUrl before register() fires the
147
149
  // HTTP POST, so that any token refresh triggered during registration can
148
150
  // be routed via connections without falling back to a locusInfo URL scan.
@@ -153,7 +155,7 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
153
155
  _this.connections.set(sessionId, sessionData);
154
156
  }
155
157
  return _this.register(datachannelUrl, datachannelToken, sessionId).then(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
156
- var sessionData, isDataChannelTokenEnabled, connectUrl;
158
+ var clientLLMDatachannelResponseTime, sessionData, error, isDataChannelTokenEnabled, connectUrl, connectStart, clientLLMWebSocketConnectTime, _t;
157
159
  return _regenerator.default.wrap(function (_context2) {
158
160
  while (1) switch (_context2.prev = _context2.next) {
159
161
  case 0:
@@ -163,21 +165,55 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
163
165
  }
164
166
  return _context2.abrupt("return", undefined);
165
167
  case 1:
166
- // locusUrl and datachannelUrl were pre-populated before register(); here
168
+ clientLLMDatachannelResponseTime = Math.round(performance.now() - registerStart); // locusUrl and datachannelUrl were pre-populated before register(); here
167
169
  // we only need to read the existing session data to get webSocketUrl/binding
168
170
  // that register() filled in.
169
171
  sessionData = _this.connections.get(sessionId) || {};
170
- _context2.next = 2;
171
- return _this.isDataChannelTokenEnabled();
172
+ if (sessionData.webSocketUrl) {
173
+ _context2.next = 2;
174
+ break;
175
+ }
176
+ // register() succeeded but the response carried no websocket URL. Attach the measured
177
+ // datachannel time so callers don't misreport a registration that completed as time 0.
178
+ error = new Error("LLM registration for ".concat(sessionId, " returned no websocket URL"));
179
+ error.timing = {
180
+ clientLLMDatachannelResponseTime: clientLLMDatachannelResponseTime
181
+ };
182
+ throw error;
172
183
  case 2:
184
+ _context2.next = 3;
185
+ return _this.isDataChannelTokenEnabled();
186
+ case 3:
173
187
  isDataChannelTokenEnabled = _context2.sent;
174
188
  connectUrl = isDataChannelTokenEnabled ? LLMChannel.buildUrlWithAwareSubchannels(sessionData.webSocketUrl, _constants.AWARE_DATA_CHANNEL) : sessionData.webSocketUrl;
175
- return _context2.abrupt("return", _this.connect(connectUrl, sessionId));
176
- case 3:
189
+ connectStart = performance.now();
190
+ _context2.prev = 4;
191
+ _context2.next = 5;
192
+ return _this.connect(connectUrl, sessionId);
193
+ case 5:
194
+ _context2.next = 7;
195
+ break;
196
+ case 6:
197
+ _context2.prev = 6;
198
+ _t = _context2["catch"](4);
199
+ // register() succeeded; only connect() failed. Attach the measured datachannel time so
200
+ // callers don't misreport a websocket failure as a registration that never completed.
201
+ // @ts-ignore
202
+ _t.timing = {
203
+ clientLLMDatachannelResponseTime: clientLLMDatachannelResponseTime
204
+ };
205
+ throw _t;
206
+ case 7:
207
+ clientLLMWebSocketConnectTime = Math.round(performance.now() - connectStart);
208
+ return _context2.abrupt("return", {
209
+ clientLLMDatachannelResponseTime: clientLLMDatachannelResponseTime,
210
+ clientLLMWebSocketConnectTime: clientLLMWebSocketConnectTime
211
+ });
212
+ case 8:
177
213
  case "end":
178
214
  return _context2.stop();
179
215
  }
180
- }, _callee2);
216
+ }, _callee2, null, [[4, 6]]);
181
217
  })));
182
218
  });
183
219
  /**
@@ -220,6 +256,16 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
220
256
  var sessionData = _this.connections.get(sessionId);
221
257
  return sessionData === null || sessionData === void 0 ? void 0 : sessionData.datachannelUrl;
222
258
  });
259
+ /**
260
+ * Get WebSocket URL for the connection
261
+ * @param {string} sessionId - Connection identifier
262
+ * @returns {string | undefined} WebSocket URL
263
+ */
264
+ (0, _defineProperty3.default)(_this, "getWebSocketUrl", function () {
265
+ var sessionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.LLM_DEFAULT_SESSION;
266
+ var sessionData = _this.connections.get(sessionId);
267
+ return sessionData === null || sessionData === void 0 ? void 0 : sessionData.webSocketUrl;
268
+ });
223
269
  /**
224
270
  * Set the owner meeting ID for a given LLM session. Used by the meetings
225
271
  * plugin to tag which Meeting instance currently owns the (default) LLM
@@ -441,7 +487,7 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
441
487
  refreshHandler,
442
488
  res,
443
489
  _args3 = arguments,
444
- _t;
490
+ _t2;
445
491
  return _regenerator.default.wrap(function (_context3) {
446
492
  while (1) switch (_context3.prev = _context3.next) {
447
493
  case 0:
@@ -462,8 +508,8 @@ var LLMChannel = exports.default = /*#__PURE__*/function (_ref) {
462
508
  return _context3.abrupt("return", res);
463
509
  case 3:
464
510
  _context3.prev = 3;
465
- _t = _context3["catch"](1);
466
- this.logger.warn("llm#refreshDataChannelToken --> DataChannel token refresh failed (likely locus changed or participant left): ".concat((_t === null || _t === void 0 ? void 0 : _t.message) || _t));
511
+ _t2 = _context3["catch"](1);
512
+ this.logger.warn("llm#refreshDataChannelToken --> DataChannel token refresh failed (likely locus changed or participant left): ".concat((_t2 === null || _t2 === void 0 ? void 0 : _t2.message) || _t2));
467
513
  return _context3.abrupt("return", null);
468
514
  case 4:
469
515
  case "end":
package/dist/llm.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":["_internalPluginMercury","_interopRequireDefault","require","_constants","_llm","_createForOfIteratorHelper","r","e","t","_Symbol","_Symbol$iterator","_Array$isArray","_unsupportedIterableToArray","length","_n","F","s","n","done","value","f","TypeError","o","a","u","call","next","return","_arrayLikeToArray","toString","slice","constructor","name","_Array$from","test","Array","_callSuper","_getPrototypeOf2","default","_possibleConstructorReturn2","_isNativeReflectConstruct","_Reflect$construct","apply","Boolean","prototype","valueOf","config","exports","llm","pingInterval","process","env","MERCURY_PING_INTERVAL","pongTimeout","MERCURY_PONG_TIMEOUT","backoffTimeMax","MERCURY_BACKOFF_TIME_MAX","backoffTimeReset","MERCURY_BACKOFF_TIME_RESET","forceCloseDelay","MERCURY_FORCE_CLOSE_DELAY","LLMChannel","_ref","_this","_classCallCheck2","_len","arguments","args","_key","concat","_defineProperty3","LLM","LLM_DEFAULT_SESSION","_map","DataChannelTokenType","Default","undefined","PracticeSession","_ref2","_asyncToGenerator2","_regenerator","mark","_callee","llmSocketUrl","datachannelToken","sessionId","isDataChannelTokenEnabled","_args","wrap","_context","prev","sent","abrupt","request","method","url","body","deviceUrl","webex","internal","device","headers","then","res","sessionData","connections","get","webSocketUrl","binding","set","catch","error","logger","stop","_x","_x2","locusUrl","datachannelUrl","register","_callee2","connectUrl","_context2","buildUrlWithAwareSubchannels","AWARE_DATA_CHANNEL","connect","socket","getSocket","connected","ownerMeetingId","currentOwner","getOwnerMeetingId","isOwner","tokenKey","resolvedTokenKey","_this$resolveSessionO","resolveSessionOwnership","info","datachannelTokens","_this$resolveSessionO2","_this$resolveSessionO3","options","resolvedSessionId","resolvedOwnerMeetingId","warn","_this$resolveSessionO4","_promise","resolve","disconnect","setOwnerMeetingId","delete","disconnectAll","clear","_inherits2","_createClass2","key","setRefreshHandler","handler","_this$resolveSessionO5","refreshHandler","_refreshDataChannelToken","_callee3","_this$connections$get","_args3","_t","_context3","message","refreshDataChannelToken","getLocusUrlByDatachannelUrl","requestUrl","_iterator","_step","_step$value","_slicedToArray2","connection","matchesDatachannelRequestUrl","err","getSessionIdByDatachannelUrl","_iterator2","_step2","_step2$value","feature","getFeature","DATA_CHANNEL_WITH_JWT_TOKEN","registrationUrl","startsWith","URL","registration","pathname","Mercury","baseUrl","subchannels","urlObj","searchParams","SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM","join"],"sources":["llm.ts"],"sourcesContent":["/* eslint-disable consistent-return */\n\nimport Mercury from '@webex/internal-plugin-mercury';\n\n// eslint-disable-next-line no-unused-vars\nimport {\n LLM,\n DATA_CHANNEL_WITH_JWT_TOKEN,\n AWARE_DATA_CHANNEL,\n SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM,\n LLM_DEFAULT_SESSION,\n} from './constants';\nimport {ILLMChannel, DataChannelTokenType} from './llm.types';\n\nexport const config = {\n llm: {\n /**\n * Milliseconds between pings sent up the socket\n * @type {number}\n */\n pingInterval: process.env.MERCURY_PING_INTERVAL || 15000,\n /**\n * Milliseconds to wait for a pong before declaring the connection dead\n * @type {number}\n */\n pongTimeout: process.env.MERCURY_PONG_TIMEOUT || 14000,\n /**\n * Maximum milliseconds between connection attempts\n * @type {Number}\n */\n backoffTimeMax: process.env.MERCURY_BACKOFF_TIME_MAX || 32000,\n /**\n * Initial milliseconds between connection attempts\n * @type {Number}\n */\n backoffTimeReset: process.env.MERCURY_BACKOFF_TIME_RESET || 1000,\n /**\n * Milliseconds to wait for a close frame before declaring the socket dead and\n * discarding it\n * @type {[type]}\n */\n forceCloseDelay: process.env.MERCURY_FORCE_CLOSE_DELAY || 2000,\n },\n};\n\n/**\n * LLMChannel to provide socket connections\n */\nexport default class LLMChannel extends (Mercury as any) implements ILLMChannel {\n namespace = LLM;\n defaultSessionId = LLM_DEFAULT_SESSION;\n /**\n * Map to store connection-specific data for multiple LLM connections\n * Key: sessionId\n * @private\n * @type {Map<string, {webSocketUrl?: string; binding?: string; locusUrl?: string; datachannelUrl?: string}>}\n */\n private connections: Map<\n string,\n {\n webSocketUrl?: string;\n binding?: string;\n locusUrl?: string;\n datachannelUrl?: string;\n ownerMeetingId?: string;\n refreshHandler?: () => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n }>;\n }\n > = new Map();\n\n // Session-keyed token cache is intentionally decoupled from connection state.\n // Disconnecting a socket session must not implicitly wipe token cache.\n private datachannelTokens: Record<string, string | undefined> = {\n [DataChannelTokenType.Default]: undefined,\n [DataChannelTokenType.PracticeSession]: undefined,\n };\n\n /**\n * Register to the websocket\n * @param {string} llmSocketUrl\n * @param {string} datachannelToken\n * @param {string} sessionId - Connection identifier\n * @returns {Promise<void>}\n */\n private register = async (\n llmSocketUrl: string,\n datachannelToken?: string,\n sessionId: string = LLM_DEFAULT_SESSION\n ): Promise<void> => {\n const isDataChannelTokenEnabled = await this.isDataChannelTokenEnabled();\n\n return this.request({\n method: 'POST',\n url: llmSocketUrl,\n body: {deviceUrl: this.webex.internal.device.url},\n headers:\n isDataChannelTokenEnabled && datachannelToken\n ? {'Data-Channel-Auth-Token': datachannelToken}\n : {},\n })\n .then((res: {body: {webSocketUrl: string; binding: string}}) => {\n // Get or create connection data\n const sessionData = this.connections.get(sessionId) || {};\n sessionData.webSocketUrl = res.body.webSocketUrl;\n sessionData.binding = res.body.binding;\n this.connections.set(sessionId, sessionData);\n })\n .catch((error: any) => {\n this.logger.error(`Error connecting to websocket for ${sessionId}: ${error}`);\n throw error;\n });\n };\n\n /**\n * Register and connect to the websocket\n * @param {string} locusUrl\n * @param {string} datachannelUrl\n * @param {string} datachannelToken\n * @param {string} sessionId - Connection identifier\n * @returns {Promise<void>}\n */\n public registerAndConnect = (\n locusUrl: string,\n datachannelUrl: string,\n datachannelToken?: string,\n sessionId: string = LLM_DEFAULT_SESSION\n ): Promise<void> => {\n // Pre-populate locusUrl and datachannelUrl before register() fires the\n // HTTP POST, so that any token refresh triggered during registration can\n // be routed via connections without falling back to a locusInfo URL scan.\n if (locusUrl && datachannelUrl) {\n const sessionData = this.connections.get(sessionId) || {};\n sessionData.locusUrl = locusUrl;\n sessionData.datachannelUrl = datachannelUrl;\n this.connections.set(sessionId, sessionData);\n }\n\n return this.register(datachannelUrl, datachannelToken, sessionId).then(async () => {\n if (!locusUrl || !datachannelUrl) return undefined;\n\n // locusUrl and datachannelUrl were pre-populated before register(); here\n // we only need to read the existing session data to get webSocketUrl/binding\n // that register() filled in.\n const sessionData = this.connections.get(sessionId) || {};\n\n const isDataChannelTokenEnabled = await this.isDataChannelTokenEnabled();\n\n const connectUrl = isDataChannelTokenEnabled\n ? LLMChannel.buildUrlWithAwareSubchannels(sessionData.webSocketUrl, AWARE_DATA_CHANNEL)\n : sessionData.webSocketUrl;\n\n return this.connect(connectUrl, sessionId);\n });\n };\n\n /**\n * Tells if LLM socket is connected\n * @param {string} sessionId - Connection identifier\n * @returns {boolean} connected\n */\n public isConnected = (sessionId = LLM_DEFAULT_SESSION): boolean => {\n const socket = this.getSocket(sessionId);\n\n return socket ? socket.connected : false;\n };\n\n /**\n * Tells if LLM socket is binding\n * @param {string} sessionId - Connection identifier\n * @returns {string} binding\n */\n public getBinding = (sessionId = LLM_DEFAULT_SESSION): string => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.binding;\n };\n\n /**\n * Get Locus URL for the connection\n * @param {string} sessionId - Connection identifier\n * @returns {string} locus Url\n */\n public getLocusUrl = (sessionId = LLM_DEFAULT_SESSION): string => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.locusUrl;\n };\n\n /**\n * Get data channel URL for the connection\n * @param {string} sessionId - Connection identifier\n * @returns {string} data channel Url\n */\n public getDatachannelUrl = (sessionId = LLM_DEFAULT_SESSION): string => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.datachannelUrl;\n };\n\n /**\n * Set the owner meeting ID for a given LLM session. Used by the meetings\n * plugin to tag which Meeting instance currently owns the (default) LLM\n * connection so that other Meeting instances can avoid disconnecting or\n * re-initializing a connection they do not own.\n *\n * Does NOT create a connections entry if one does not already exist — this\n * method is a no-op when there is no active session data. Callers should\n * invoke it after a successful `registerAndConnect` or during an explicit\n * ownership handoff.\n *\n * @param {string | undefined} ownerMeetingId - Meeting ID (or undefined to clear)\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {void}\n */\n public setOwnerMeetingId = (\n ownerMeetingId: string | undefined,\n sessionId: string = LLM_DEFAULT_SESSION\n ): void => {\n const sessionData = this.connections.get(sessionId);\n\n if (!sessionData) {\n return;\n }\n\n sessionData.ownerMeetingId = ownerMeetingId;\n this.connections.set(sessionId, sessionData);\n };\n\n /**\n * Get the owner meeting ID currently associated with an LLM session.\n * Returns undefined when no owner has been assigned (e.g. before the\n * first successful `registerAndConnect`, or after `disconnectLLM`).\n *\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {string | undefined} ownerMeetingId\n */\n public getOwnerMeetingId = (sessionId: string = LLM_DEFAULT_SESSION): string | undefined => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.ownerMeetingId;\n };\n\n /**\n * Resolve ownership information for an LLM session.\n *\n * Rules:\n * - no current owner => caller may proceed\n * - caller has no identity to assert => treat as owner\n * - otherwise caller must match current owner\n *\n * @param {string | undefined} ownerMeetingId - Candidate owner to evaluate\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {{currentOwner: (string|undefined), isOwner: boolean}}\n */\n public resolveSessionOwnership = (\n ownerMeetingId?: string,\n sessionId: string = LLM_DEFAULT_SESSION\n ): {\n currentOwner: string | undefined;\n isOwner: boolean;\n } => {\n const currentOwner = this.getOwnerMeetingId(sessionId);\n const isOwner = !currentOwner || !ownerMeetingId || currentOwner === ownerMeetingId;\n\n return {\n currentOwner,\n isOwner,\n };\n };\n\n /**\n * Get data channel token for the connection\n * @param {DataChannelTokenType|string} tokenKey\n * @param {string | undefined} ownerMeetingId - Meeting id asserting read ownership\n * @returns {string | undefined} data channel token\n */\n public getDatachannelToken = (\n tokenKey?: DataChannelTokenType | string,\n ownerMeetingId?: string\n ): string | undefined => {\n const resolvedTokenKey = tokenKey ?? DataChannelTokenType.Default;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey);\n\n if (!isOwner) {\n this.logger.info(\n `llm#getDatachannelToken --> skip read for session ${resolvedTokenKey}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return undefined;\n }\n\n return this.datachannelTokens[resolvedTokenKey];\n };\n\n /**\n * Set data channel token for the connection\n * @param {string} datachannelToken - data channel token\n * @param {DataChannelTokenType|string} [tokenKey]\n * @param {string | undefined} ownerMeetingId - Meeting id asserting write ownership\n * @returns {void}\n */\n public setDatachannelToken = (\n datachannelToken: string,\n tokenKey?: DataChannelTokenType | string,\n ownerMeetingId?: string\n ): void => {\n const resolvedTokenKey = tokenKey ?? DataChannelTokenType.Default;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey);\n\n if (!isOwner) {\n this.logger.info(\n `llm#setDatachannelToken --> skip write for session ${resolvedTokenKey}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return;\n }\n\n this.datachannelTokens[resolvedTokenKey] = datachannelToken;\n };\n\n /**\n * Clears a single session's data channel token.\n * @param {DataChannelTokenType|string} tokenKey\n * @param {string} ownerMeetingId - Meeting id asserting delete ownership\n * @returns {void}\n */\n public clearDatachannelToken = (\n tokenKey: DataChannelTokenType | string,\n ownerMeetingId: string\n ): void => {\n const resolvedTokenKey = tokenKey;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey);\n\n if (!isOwner) {\n this.logger.info(\n `llm#clearDatachannelToken --> skip clear for session ${resolvedTokenKey}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return;\n }\n\n this.datachannelTokens[resolvedTokenKey] = undefined;\n delete this.datachannelTokens[resolvedTokenKey];\n };\n\n /**\n * Set the handler used to refresh the DataChannel token\n *\n * @param {function} handler - Function that returns a refreshed token\n * @param {string} [sessionId] - Connection identifier\n * @param {string | undefined} ownerMeetingId - Meeting id asserting refresh-handler ownership\n * @returns {void}\n */\n public setRefreshHandler(\n handler: () => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n }>,\n sessionId?: string,\n ownerMeetingId?: string\n ) {\n const resolvedSessionId = sessionId ?? LLM_DEFAULT_SESSION;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedSessionId);\n\n if (!isOwner) {\n this.logger.info(\n `llm#setRefreshHandler --> skip write for session ${resolvedSessionId}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return;\n }\n\n const sessionData = this.connections.get(resolvedSessionId);\n\n if (sessionData) {\n sessionData.refreshHandler = handler;\n if (ownerMeetingId) {\n sessionData.ownerMeetingId = ownerMeetingId;\n }\n\n return;\n }\n\n // Intentionally allow a pre-connection session shape here.\n // Some flows inject refreshHandler before register/connect so token refresh\n // is already wired when the socket lifecycle starts. register()/\n // registerAndConnect() will later fill webSocketUrl/binding/locusUrl/\n // datachannelUrl into this same session entry.\n this.connections.set(resolvedSessionId, {\n refreshHandler: handler,\n ownerMeetingId,\n });\n }\n\n /**\n * Refresh the data channel token using the injected handler.\n * Logs a descriptive error if the handler is missing or fails.\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {Promise<string>} The refreshed token.\n */\n public async refreshDataChannelToken(sessionId: string = LLM_DEFAULT_SESSION) {\n const refreshHandler = this.connections.get(sessionId)?.refreshHandler;\n\n if (!refreshHandler) {\n this.logger.warn(\n `llm#refreshDataChannelToken --> LLM refreshHandler is not set for session ${sessionId}, skipping token refresh`\n );\n\n return null;\n }\n\n try {\n const res = await refreshHandler();\n\n return res;\n } catch (error: any) {\n this.logger.warn(\n `llm#refreshDataChannelToken --> DataChannel token refresh failed (likely locus changed or participant left): ${\n error?.message || error\n }`\n );\n\n return null;\n }\n }\n\n /**\n * Disconnects websocket connection\n * @param {{code: number, reason: string}} options - The disconnect option object with code and reason\n * @param {string} sessionId - Connection identifier\n * @param {string} ownerMeetingId - Meeting id asserting disconnect ownership\n * @returns {Promise<boolean>} True when disconnect was performed, false when skipped\n */\n public disconnectLLM = (\n options: {code: number; reason: string},\n sessionId?: string,\n ownerMeetingId?: string\n ): Promise<boolean> => {\n const resolvedSessionId = sessionId ?? LLM_DEFAULT_SESSION;\n\n // Backward-compat path: historically callers could omit ownerMeetingId\n // (and sometimes sessionId). Reuse current owner when available so legacy\n // calls remain best-effort without throwing at teardown time.\n const resolvedOwnerMeetingId = ownerMeetingId || this.getOwnerMeetingId(resolvedSessionId);\n\n if (!ownerMeetingId) {\n this.logger.warn(\n `llm#disconnectLLM --> ownerMeetingId is omitted for session ${resolvedSessionId}; using legacy compatibility path`\n );\n }\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(\n resolvedOwnerMeetingId,\n resolvedSessionId\n );\n\n if (!isOwner) {\n this.logger.info(\n `llm#disconnectLLM --> skip disconnect for session ${resolvedSessionId}; owned by ${currentOwner}, candidate ${resolvedOwnerMeetingId}`\n );\n\n return Promise.resolve(false);\n }\n\n return this.disconnect(options, resolvedSessionId).then(() => {\n // Clear owner tag before cleanup to ensure it's not lingering\n // if another meeting claimed it during disconnect\n this.setOwnerMeetingId(undefined, resolvedSessionId);\n\n // Clean up sessions data\n this.connections.delete(resolvedSessionId);\n\n return true;\n });\n };\n\n /**\n * Disconnects all LLM websocket connections\n * @param {{code: number, reason: string}} options - The disconnect option object with code and reason\n * @returns {Promise<void>}\n */\n public disconnectAllLLM = (options?: {code: number; reason: string}): Promise<void> =>\n this.disconnectAll(options).then(() => {\n // Clean up all connection data\n this.connections.clear();\n });\n\n /**\n * Get all active LLM connections\n * @returns {Map} Map of sessionId to session data\n */\n public getAllConnections = (): Map<\n string,\n {\n webSocketUrl?: string;\n binding?: string;\n locusUrl?: string;\n datachannelUrl?: string;\n ownerMeetingId?: string;\n }\n > => new Map(this.connections);\n\n /**\n * Look up the locusUrl associated with a datachannel request URL.\n * Iterates all active LLM sessions and returns the locusUrl of the\n * session whose stored datachannelUrl is a prefix of the given request URL.\n *\n * @param {string} requestUrl - The in-flight request URL to match\n * @returns {string | undefined} The matching locusUrl, or undefined if not found\n */\n public getLocusUrlByDatachannelUrl(requestUrl: string): string | undefined {\n for (const [, connection] of this.connections) {\n if (\n connection.datachannelUrl &&\n LLMChannel.matchesDatachannelRequestUrl(requestUrl, connection.datachannelUrl)\n ) {\n return connection.locusUrl;\n }\n }\n\n return undefined;\n }\n\n /**\n * Look up the sessionId associated with a datachannel request URL.\n * Iterates all active LLM sessions and returns the sessionId whose\n * stored datachannelUrl is a prefix of the given request URL.\n *\n * @param {string} requestUrl - The in-flight request URL to match\n * @returns {string | undefined} The matching sessionId, or undefined if not found\n */\n public getSessionIdByDatachannelUrl(requestUrl: string): string | undefined {\n for (const [sessionId, connection] of this.connections) {\n if (\n connection.datachannelUrl &&\n LLMChannel.matchesDatachannelRequestUrl(requestUrl, connection.datachannelUrl)\n ) {\n return sessionId;\n }\n }\n\n return undefined;\n }\n\n /**\n * Matches a request URL to a stored datachannel registration URL.\n * Host can differ (e.g. rewritten by hostmap interceptor), so we first\n * try full URL prefix and then fall back to pathname prefix.\n * @param {string} requestUrl\n * @param {string} registrationUrl\n * @returns {boolean}\n */\n public static matchesDatachannelRequestUrl(requestUrl: string, registrationUrl: string): boolean {\n if (!requestUrl || !registrationUrl) {\n return false;\n }\n\n if (requestUrl.startsWith(registrationUrl)) {\n return true;\n }\n\n try {\n const request = new URL(requestUrl);\n const registration = new URL(registrationUrl);\n\n return request.pathname.startsWith(registration.pathname);\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Returns true if data channel token is enabled, false otherwise\n * @returns {Promise<boolean>} resolves with true if data channel token is enabled\n */\n public isDataChannelTokenEnabled(): Promise<boolean> {\n // @ts-ignore\n return this.webex.internal.feature.getFeature('developer', DATA_CHANNEL_WITH_JWT_TOKEN);\n }\n\n /**\n * Builds a WebSocket URL with the `subscriptionAwareSubchannels` query parameter.\n *\n * @param {string} baseUrl - The original WebSocket URL.\n * @param {string[]} subchannels - List of subchannels to declare as subscription-aware.\n * @returns {string} The final URL with updated query parameters.\n */\n\n public static buildUrlWithAwareSubchannels = (baseUrl: string, subchannels: string[]) => {\n const urlObj = new URL(baseUrl);\n urlObj.searchParams.set(SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM, subchannels.join(','));\n\n return urlObj.toString();\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_internalPluginMercury","_interopRequireDefault","require","_constants","_llm","_createForOfIteratorHelper","r","e","t","_Symbol","_Symbol$iterator","_Array$isArray","_unsupportedIterableToArray","length","_n","F","s","n","done","value","f","TypeError","o","a","u","call","next","return","_arrayLikeToArray","toString","slice","constructor","name","_Array$from","test","Array","_callSuper","_getPrototypeOf2","default","_possibleConstructorReturn2","_isNativeReflectConstruct","_Reflect$construct","apply","Boolean","prototype","valueOf","config","exports","llm","pingInterval","process","env","MERCURY_PING_INTERVAL","pongTimeout","MERCURY_PONG_TIMEOUT","backoffTimeMax","MERCURY_BACKOFF_TIME_MAX","backoffTimeReset","MERCURY_BACKOFF_TIME_RESET","forceCloseDelay","MERCURY_FORCE_CLOSE_DELAY","LLMChannel","_ref","_this","_classCallCheck2","_len","arguments","args","_key","concat","_defineProperty3","LLM","LLM_DEFAULT_SESSION","_map","DataChannelTokenType","Default","undefined","PracticeSession","_ref2","_asyncToGenerator2","_regenerator","mark","_callee","llmSocketUrl","datachannelToken","sessionId","isDataChannelTokenEnabled","_args","wrap","_context","prev","sent","abrupt","request","method","url","body","deviceUrl","webex","internal","device","headers","then","res","sessionData","connections","get","webSocketUrl","binding","set","catch","error","logger","stop","_x","_x2","locusUrl","datachannelUrl","registerStart","performance","now","register","_callee2","clientLLMDatachannelResponseTime","connectUrl","connectStart","clientLLMWebSocketConnectTime","_t","_context2","Math","round","Error","timing","buildUrlWithAwareSubchannels","AWARE_DATA_CHANNEL","connect","socket","getSocket","connected","ownerMeetingId","currentOwner","getOwnerMeetingId","isOwner","tokenKey","resolvedTokenKey","_this$resolveSessionO","resolveSessionOwnership","info","datachannelTokens","_this$resolveSessionO2","_this$resolveSessionO3","options","resolvedSessionId","resolvedOwnerMeetingId","warn","_this$resolveSessionO4","_promise","resolve","disconnect","setOwnerMeetingId","delete","disconnectAll","clear","_inherits2","_createClass2","key","setRefreshHandler","handler","_this$resolveSessionO5","refreshHandler","_refreshDataChannelToken","_callee3","_this$connections$get","_args3","_t2","_context3","message","refreshDataChannelToken","getLocusUrlByDatachannelUrl","requestUrl","_iterator","_step","_step$value","_slicedToArray2","connection","matchesDatachannelRequestUrl","err","getSessionIdByDatachannelUrl","_iterator2","_step2","_step2$value","feature","getFeature","DATA_CHANNEL_WITH_JWT_TOKEN","registrationUrl","startsWith","URL","registration","pathname","Mercury","baseUrl","subchannels","urlObj","searchParams","SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM","join"],"sources":["llm.ts"],"sourcesContent":["/* eslint-disable consistent-return */\n\nimport Mercury from '@webex/internal-plugin-mercury';\n\n// eslint-disable-next-line no-unused-vars\nimport {\n LLM,\n DATA_CHANNEL_WITH_JWT_TOKEN,\n AWARE_DATA_CHANNEL,\n SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM,\n LLM_DEFAULT_SESSION,\n} from './constants';\nimport {ILLMChannel, DataChannelTokenType, RegisterAndConnectTiming} from './llm.types';\n\nexport const config = {\n llm: {\n /**\n * Milliseconds between pings sent up the socket\n * @type {number}\n */\n pingInterval: process.env.MERCURY_PING_INTERVAL || 15000,\n /**\n * Milliseconds to wait for a pong before declaring the connection dead\n * @type {number}\n */\n pongTimeout: process.env.MERCURY_PONG_TIMEOUT || 14000,\n /**\n * Maximum milliseconds between connection attempts\n * @type {Number}\n */\n backoffTimeMax: process.env.MERCURY_BACKOFF_TIME_MAX || 32000,\n /**\n * Initial milliseconds between connection attempts\n * @type {Number}\n */\n backoffTimeReset: process.env.MERCURY_BACKOFF_TIME_RESET || 1000,\n /**\n * Milliseconds to wait for a close frame before declaring the socket dead and\n * discarding it\n * @type {[type]}\n */\n forceCloseDelay: process.env.MERCURY_FORCE_CLOSE_DELAY || 2000,\n },\n};\n\n/**\n * LLMChannel to provide socket connections\n */\nexport default class LLMChannel extends (Mercury as any) implements ILLMChannel {\n namespace = LLM;\n defaultSessionId = LLM_DEFAULT_SESSION;\n /**\n * Map to store connection-specific data for multiple LLM connections\n * Key: sessionId\n * @private\n * @type {Map<string, {webSocketUrl?: string; binding?: string; locusUrl?: string; datachannelUrl?: string}>}\n */\n private connections: Map<\n string,\n {\n webSocketUrl?: string;\n binding?: string;\n locusUrl?: string;\n datachannelUrl?: string;\n ownerMeetingId?: string;\n refreshHandler?: () => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n }>;\n }\n > = new Map();\n\n // Session-keyed token cache is intentionally decoupled from connection state.\n // Disconnecting a socket session must not implicitly wipe token cache.\n private datachannelTokens: Record<string, string | undefined> = {\n [DataChannelTokenType.Default]: undefined,\n [DataChannelTokenType.PracticeSession]: undefined,\n };\n\n /**\n * Register to the websocket\n * @param {string} llmSocketUrl\n * @param {string} datachannelToken\n * @param {string} sessionId - Connection identifier\n * @returns {Promise<void>}\n */\n private register = async (\n llmSocketUrl: string,\n datachannelToken?: string,\n sessionId: string = LLM_DEFAULT_SESSION\n ): Promise<void> => {\n const isDataChannelTokenEnabled = await this.isDataChannelTokenEnabled();\n\n return this.request({\n method: 'POST',\n url: llmSocketUrl,\n body: {deviceUrl: this.webex.internal.device.url},\n headers:\n isDataChannelTokenEnabled && datachannelToken\n ? {'Data-Channel-Auth-Token': datachannelToken}\n : {},\n })\n .then((res: {body: {webSocketUrl: string; binding: string}}) => {\n // Get or create connection data\n const sessionData = this.connections.get(sessionId) || {};\n sessionData.webSocketUrl = res.body.webSocketUrl;\n sessionData.binding = res.body.binding;\n this.connections.set(sessionId, sessionData);\n })\n .catch((error: any) => {\n this.logger.error(`Error connecting to websocket for ${sessionId}: ${error}`);\n throw error;\n });\n };\n\n /**\n * Register and connect to the websocket\n * @param {string} locusUrl\n * @param {string} datachannelUrl\n * @param {string} datachannelToken\n * @param {string} sessionId - Connection identifier\n * @returns {Promise<void>}\n */\n public registerAndConnect = (\n locusUrl: string,\n datachannelUrl: string,\n datachannelToken?: string,\n sessionId: string = LLM_DEFAULT_SESSION\n ): Promise<RegisterAndConnectTiming | undefined> => {\n const registerStart = performance.now();\n\n // Pre-populate locusUrl and datachannelUrl before register() fires the\n // HTTP POST, so that any token refresh triggered during registration can\n // be routed via connections without falling back to a locusInfo URL scan.\n if (locusUrl && datachannelUrl) {\n const sessionData = this.connections.get(sessionId) || {};\n sessionData.locusUrl = locusUrl;\n sessionData.datachannelUrl = datachannelUrl;\n this.connections.set(sessionId, sessionData);\n }\n\n return this.register(datachannelUrl, datachannelToken, sessionId).then(async () => {\n if (!locusUrl || !datachannelUrl) return undefined;\n\n const clientLLMDatachannelResponseTime = Math.round(performance.now() - registerStart);\n\n // locusUrl and datachannelUrl were pre-populated before register(); here\n // we only need to read the existing session data to get webSocketUrl/binding\n // that register() filled in.\n const sessionData = this.connections.get(sessionId) || {};\n\n if (!sessionData.webSocketUrl) {\n // register() succeeded but the response carried no websocket URL. Attach the measured\n // datachannel time so callers don't misreport a registration that completed as time 0.\n const error: any = new Error(`LLM registration for ${sessionId} returned no websocket URL`);\n error.timing = {clientLLMDatachannelResponseTime};\n throw error;\n }\n\n const isDataChannelTokenEnabled = await this.isDataChannelTokenEnabled();\n\n const connectUrl = isDataChannelTokenEnabled\n ? LLMChannel.buildUrlWithAwareSubchannels(sessionData.webSocketUrl, AWARE_DATA_CHANNEL)\n : sessionData.webSocketUrl;\n\n const connectStart = performance.now();\n\n try {\n await this.connect(connectUrl, sessionId);\n } catch (error) {\n // register() succeeded; only connect() failed. Attach the measured datachannel time so\n // callers don't misreport a websocket failure as a registration that never completed.\n // @ts-ignore\n error.timing = {clientLLMDatachannelResponseTime};\n throw error;\n }\n\n const clientLLMWebSocketConnectTime = Math.round(performance.now() - connectStart);\n\n return {clientLLMDatachannelResponseTime, clientLLMWebSocketConnectTime};\n });\n };\n\n /**\n * Tells if LLM socket is connected\n * @param {string} sessionId - Connection identifier\n * @returns {boolean} connected\n */\n public isConnected = (sessionId = LLM_DEFAULT_SESSION): boolean => {\n const socket = this.getSocket(sessionId);\n\n return socket ? socket.connected : false;\n };\n\n /**\n * Tells if LLM socket is binding\n * @param {string} sessionId - Connection identifier\n * @returns {string} binding\n */\n public getBinding = (sessionId = LLM_DEFAULT_SESSION): string => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.binding;\n };\n\n /**\n * Get Locus URL for the connection\n * @param {string} sessionId - Connection identifier\n * @returns {string} locus Url\n */\n public getLocusUrl = (sessionId = LLM_DEFAULT_SESSION): string => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.locusUrl;\n };\n\n /**\n * Get data channel URL for the connection\n * @param {string} sessionId - Connection identifier\n * @returns {string} data channel Url\n */\n public getDatachannelUrl = (sessionId = LLM_DEFAULT_SESSION): string => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.datachannelUrl;\n };\n\n /**\n * Get WebSocket URL for the connection\n * @param {string} sessionId - Connection identifier\n * @returns {string | undefined} WebSocket URL\n */\n public getWebSocketUrl = (sessionId = LLM_DEFAULT_SESSION): string | undefined => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.webSocketUrl;\n };\n\n /**\n * Set the owner meeting ID for a given LLM session. Used by the meetings\n * plugin to tag which Meeting instance currently owns the (default) LLM\n * connection so that other Meeting instances can avoid disconnecting or\n * re-initializing a connection they do not own.\n *\n * Does NOT create a connections entry if one does not already exist — this\n * method is a no-op when there is no active session data. Callers should\n * invoke it after a successful `registerAndConnect` or during an explicit\n * ownership handoff.\n *\n * @param {string | undefined} ownerMeetingId - Meeting ID (or undefined to clear)\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {void}\n */\n public setOwnerMeetingId = (\n ownerMeetingId: string | undefined,\n sessionId: string = LLM_DEFAULT_SESSION\n ): void => {\n const sessionData = this.connections.get(sessionId);\n\n if (!sessionData) {\n return;\n }\n\n sessionData.ownerMeetingId = ownerMeetingId;\n this.connections.set(sessionId, sessionData);\n };\n\n /**\n * Get the owner meeting ID currently associated with an LLM session.\n * Returns undefined when no owner has been assigned (e.g. before the\n * first successful `registerAndConnect`, or after `disconnectLLM`).\n *\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {string | undefined} ownerMeetingId\n */\n public getOwnerMeetingId = (sessionId: string = LLM_DEFAULT_SESSION): string | undefined => {\n const sessionData = this.connections.get(sessionId);\n\n return sessionData?.ownerMeetingId;\n };\n\n /**\n * Resolve ownership information for an LLM session.\n *\n * Rules:\n * - no current owner => caller may proceed\n * - caller has no identity to assert => treat as owner\n * - otherwise caller must match current owner\n *\n * @param {string | undefined} ownerMeetingId - Candidate owner to evaluate\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {{currentOwner: (string|undefined), isOwner: boolean}}\n */\n public resolveSessionOwnership = (\n ownerMeetingId?: string,\n sessionId: string = LLM_DEFAULT_SESSION\n ): {\n currentOwner: string | undefined;\n isOwner: boolean;\n } => {\n const currentOwner = this.getOwnerMeetingId(sessionId);\n const isOwner = !currentOwner || !ownerMeetingId || currentOwner === ownerMeetingId;\n\n return {\n currentOwner,\n isOwner,\n };\n };\n\n /**\n * Get data channel token for the connection\n * @param {DataChannelTokenType|string} tokenKey\n * @param {string | undefined} ownerMeetingId - Meeting id asserting read ownership\n * @returns {string | undefined} data channel token\n */\n public getDatachannelToken = (\n tokenKey?: DataChannelTokenType | string,\n ownerMeetingId?: string\n ): string | undefined => {\n const resolvedTokenKey = tokenKey ?? DataChannelTokenType.Default;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey);\n\n if (!isOwner) {\n this.logger.info(\n `llm#getDatachannelToken --> skip read for session ${resolvedTokenKey}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return undefined;\n }\n\n return this.datachannelTokens[resolvedTokenKey];\n };\n\n /**\n * Set data channel token for the connection\n * @param {string} datachannelToken - data channel token\n * @param {DataChannelTokenType|string} [tokenKey]\n * @param {string | undefined} ownerMeetingId - Meeting id asserting write ownership\n * @returns {void}\n */\n public setDatachannelToken = (\n datachannelToken: string,\n tokenKey?: DataChannelTokenType | string,\n ownerMeetingId?: string\n ): void => {\n const resolvedTokenKey = tokenKey ?? DataChannelTokenType.Default;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey);\n\n if (!isOwner) {\n this.logger.info(\n `llm#setDatachannelToken --> skip write for session ${resolvedTokenKey}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return;\n }\n\n this.datachannelTokens[resolvedTokenKey] = datachannelToken;\n };\n\n /**\n * Clears a single session's data channel token.\n * @param {DataChannelTokenType|string} tokenKey\n * @param {string} ownerMeetingId - Meeting id asserting delete ownership\n * @returns {void}\n */\n public clearDatachannelToken = (\n tokenKey: DataChannelTokenType | string,\n ownerMeetingId: string\n ): void => {\n const resolvedTokenKey = tokenKey;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedTokenKey);\n\n if (!isOwner) {\n this.logger.info(\n `llm#clearDatachannelToken --> skip clear for session ${resolvedTokenKey}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return;\n }\n\n this.datachannelTokens[resolvedTokenKey] = undefined;\n delete this.datachannelTokens[resolvedTokenKey];\n };\n\n /**\n * Set the handler used to refresh the DataChannel token\n *\n * @param {function} handler - Function that returns a refreshed token\n * @param {string} [sessionId] - Connection identifier\n * @param {string | undefined} ownerMeetingId - Meeting id asserting refresh-handler ownership\n * @returns {void}\n */\n public setRefreshHandler(\n handler: () => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n }>,\n sessionId?: string,\n ownerMeetingId?: string\n ) {\n const resolvedSessionId = sessionId ?? LLM_DEFAULT_SESSION;\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(ownerMeetingId, resolvedSessionId);\n\n if (!isOwner) {\n this.logger.info(\n `llm#setRefreshHandler --> skip write for session ${resolvedSessionId}; owned by ${currentOwner}, candidate ${ownerMeetingId}`\n );\n\n return;\n }\n\n const sessionData = this.connections.get(resolvedSessionId);\n\n if (sessionData) {\n sessionData.refreshHandler = handler;\n if (ownerMeetingId) {\n sessionData.ownerMeetingId = ownerMeetingId;\n }\n\n return;\n }\n\n // Intentionally allow a pre-connection session shape here.\n // Some flows inject refreshHandler before register/connect so token refresh\n // is already wired when the socket lifecycle starts. register()/\n // registerAndConnect() will later fill webSocketUrl/binding/locusUrl/\n // datachannelUrl into this same session entry.\n this.connections.set(resolvedSessionId, {\n refreshHandler: handler,\n ownerMeetingId,\n });\n }\n\n /**\n * Refresh the data channel token using the injected handler.\n * Logs a descriptive error if the handler is missing or fails.\n * @param {string} sessionId - Connection identifier (defaults to default session)\n * @returns {Promise<string>} The refreshed token.\n */\n public async refreshDataChannelToken(sessionId: string = LLM_DEFAULT_SESSION) {\n const refreshHandler = this.connections.get(sessionId)?.refreshHandler;\n\n if (!refreshHandler) {\n this.logger.warn(\n `llm#refreshDataChannelToken --> LLM refreshHandler is not set for session ${sessionId}, skipping token refresh`\n );\n\n return null;\n }\n\n try {\n const res = await refreshHandler();\n\n return res;\n } catch (error: any) {\n this.logger.warn(\n `llm#refreshDataChannelToken --> DataChannel token refresh failed (likely locus changed or participant left): ${\n error?.message || error\n }`\n );\n\n return null;\n }\n }\n\n /**\n * Disconnects websocket connection\n * @param {{code: number, reason: string}} options - The disconnect option object with code and reason\n * @param {string} sessionId - Connection identifier\n * @param {string} ownerMeetingId - Meeting id asserting disconnect ownership\n * @returns {Promise<boolean>} True when disconnect was performed, false when skipped\n */\n public disconnectLLM = (\n options: {code: number; reason: string},\n sessionId?: string,\n ownerMeetingId?: string\n ): Promise<boolean> => {\n const resolvedSessionId = sessionId ?? LLM_DEFAULT_SESSION;\n\n // Backward-compat path: historically callers could omit ownerMeetingId\n // (and sometimes sessionId). Reuse current owner when available so legacy\n // calls remain best-effort without throwing at teardown time.\n const resolvedOwnerMeetingId = ownerMeetingId || this.getOwnerMeetingId(resolvedSessionId);\n\n if (!ownerMeetingId) {\n this.logger.warn(\n `llm#disconnectLLM --> ownerMeetingId is omitted for session ${resolvedSessionId}; using legacy compatibility path`\n );\n }\n\n const {currentOwner, isOwner} = this.resolveSessionOwnership(\n resolvedOwnerMeetingId,\n resolvedSessionId\n );\n\n if (!isOwner) {\n this.logger.info(\n `llm#disconnectLLM --> skip disconnect for session ${resolvedSessionId}; owned by ${currentOwner}, candidate ${resolvedOwnerMeetingId}`\n );\n\n return Promise.resolve(false);\n }\n\n return this.disconnect(options, resolvedSessionId).then(() => {\n // Clear owner tag before cleanup to ensure it's not lingering\n // if another meeting claimed it during disconnect\n this.setOwnerMeetingId(undefined, resolvedSessionId);\n\n // Clean up sessions data\n this.connections.delete(resolvedSessionId);\n\n return true;\n });\n };\n\n /**\n * Disconnects all LLM websocket connections\n * @param {{code: number, reason: string}} options - The disconnect option object with code and reason\n * @returns {Promise<void>}\n */\n public disconnectAllLLM = (options?: {code: number; reason: string}): Promise<void> =>\n this.disconnectAll(options).then(() => {\n // Clean up all connection data\n this.connections.clear();\n });\n\n /**\n * Get all active LLM connections\n * @returns {Map} Map of sessionId to session data\n */\n public getAllConnections = (): Map<\n string,\n {\n webSocketUrl?: string;\n binding?: string;\n locusUrl?: string;\n datachannelUrl?: string;\n ownerMeetingId?: string;\n }\n > => new Map(this.connections);\n\n /**\n * Look up the locusUrl associated with a datachannel request URL.\n * Iterates all active LLM sessions and returns the locusUrl of the\n * session whose stored datachannelUrl is a prefix of the given request URL.\n *\n * @param {string} requestUrl - The in-flight request URL to match\n * @returns {string | undefined} The matching locusUrl, or undefined if not found\n */\n public getLocusUrlByDatachannelUrl(requestUrl: string): string | undefined {\n for (const [, connection] of this.connections) {\n if (\n connection.datachannelUrl &&\n LLMChannel.matchesDatachannelRequestUrl(requestUrl, connection.datachannelUrl)\n ) {\n return connection.locusUrl;\n }\n }\n\n return undefined;\n }\n\n /**\n * Look up the sessionId associated with a datachannel request URL.\n * Iterates all active LLM sessions and returns the sessionId whose\n * stored datachannelUrl is a prefix of the given request URL.\n *\n * @param {string} requestUrl - The in-flight request URL to match\n * @returns {string | undefined} The matching sessionId, or undefined if not found\n */\n public getSessionIdByDatachannelUrl(requestUrl: string): string | undefined {\n for (const [sessionId, connection] of this.connections) {\n if (\n connection.datachannelUrl &&\n LLMChannel.matchesDatachannelRequestUrl(requestUrl, connection.datachannelUrl)\n ) {\n return sessionId;\n }\n }\n\n return undefined;\n }\n\n /**\n * Matches a request URL to a stored datachannel registration URL.\n * Host can differ (e.g. rewritten by hostmap interceptor), so we first\n * try full URL prefix and then fall back to pathname prefix.\n * @param {string} requestUrl\n * @param {string} registrationUrl\n * @returns {boolean}\n */\n public static matchesDatachannelRequestUrl(requestUrl: string, registrationUrl: string): boolean {\n if (!requestUrl || !registrationUrl) {\n return false;\n }\n\n if (requestUrl.startsWith(registrationUrl)) {\n return true;\n }\n\n try {\n const request = new URL(requestUrl);\n const registration = new URL(registrationUrl);\n\n return request.pathname.startsWith(registration.pathname);\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Returns true if data channel token is enabled, false otherwise\n * @returns {Promise<boolean>} resolves with true if data channel token is enabled\n */\n public isDataChannelTokenEnabled(): Promise<boolean> {\n // @ts-ignore\n return this.webex.internal.feature.getFeature('developer', DATA_CHANNEL_WITH_JWT_TOKEN);\n }\n\n /**\n * Builds a WebSocket URL with the `subscriptionAwareSubchannels` query parameter.\n *\n * @param {string} baseUrl - The original WebSocket URL.\n * @param {string[]} subchannels - List of subchannels to declare as subscription-aware.\n * @returns {string} The final URL with updated query parameters.\n */\n\n public static buildUrlWithAwareSubchannels = (baseUrl: string, subchannels: string[]) => {\n const urlObj = new URL(baseUrl);\n urlObj.searchParams.set(SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM, subchannels.join(','));\n\n return urlObj.toString();\n };\n}\n"],"mappings":"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","ignoreList":[]}
package/dist/llm.types.js CHANGED
@@ -9,5 +9,9 @@ var DataChannelTokenType = exports.DataChannelTokenType = /*#__PURE__*/function
9
9
  DataChannelTokenType["Default"] = "llm-default-session";
10
10
  DataChannelTokenType["PracticeSession"] = "llm-practice-session";
11
11
  return DataChannelTokenType;
12
- }({}); // eslint-disable-next-line import/prefer-default-export
12
+ }({});
13
+ /**
14
+ * Latencies (in milliseconds) captured during register + websocket connect.
15
+ */
16
+ // eslint-disable-next-line import/prefer-default-export
13
17
  //# sourceMappingURL=llm.types.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["DataChannelTokenType","exports"],"sources":["llm.types.ts"],"sourcesContent":["export enum DataChannelTokenType {\n Default = 'llm-default-session',\n PracticeSession = 'llm-practice-session',\n}\n\ntype DataChannelTokenKey = DataChannelTokenType | string;\n\ninterface ILLMChannel {\n registerAndConnect: (\n locusUrl: string,\n datachannelUrl: string,\n datachannelToken?: string,\n sessionId?: string\n ) => Promise<void>;\n isConnected: (sessionId?: string) => boolean;\n getBinding: (sessionId?: string) => string;\n getLocusUrl: (sessionId?: string) => string;\n getDatachannelUrl: (sessionId?: string) => string;\n disconnectLLM: (\n options: {code: number; reason: string},\n sessionId?: string,\n ownerMeetingId?: string\n ) => Promise<boolean>;\n disconnectAllLLM: (options?: {code: number; reason: string}) => Promise<void>;\n setOwnerMeetingId: (ownerMeetingId: string | undefined, sessionId?: string) => void;\n getOwnerMeetingId: (sessionId?: string) => string | undefined;\n resolveSessionOwnership: (\n ownerMeetingId?: string,\n sessionId?: string\n ) => {\n currentOwner: string | undefined;\n isOwner: boolean;\n };\n getDatachannelToken: (\n tokenKey?: DataChannelTokenKey,\n ownerMeetingId?: string\n ) => string | undefined;\n setDatachannelToken: (\n datachannelToken: string,\n tokenKey?: DataChannelTokenKey,\n ownerMeetingId?: string\n ) => void;\n clearDatachannelToken: (tokenKey: DataChannelTokenKey, ownerMeetingId: string) => void;\n setRefreshHandler: (\n handler: () => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n }>,\n sessionId?: string,\n ownerMeetingId?: string\n ) => void;\n refreshDataChannelToken: (sessionId?: string) => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n } | null>;\n getLocusUrlByDatachannelUrl: (requestUrl: string) => string | undefined;\n getSessionIdByDatachannelUrl: (requestUrl: string) => string | undefined;\n getAllConnections: () => Map<\n string,\n {\n webSocketUrl?: string;\n binding?: string;\n locusUrl?: string;\n datachannelUrl?: string;\n ownerMeetingId?: string;\n }\n >;\n}\n\n// eslint-disable-next-line import/prefer-default-export\nexport type {ILLMChannel, DataChannelTokenKey};\n"],"mappings":";;;;;;;IAAYA,oBAAoB,GAAAC,OAAA,CAAAD,oBAAA,0BAApBA,oBAAoB;EAApBA,oBAAoB;EAApBA,oBAAoB;EAAA,OAApBA,oBAAoB;AAAA,OAmEhC","ignoreList":[]}
1
+ {"version":3,"names":["DataChannelTokenType","exports"],"sources":["llm.types.ts"],"sourcesContent":["export enum DataChannelTokenType {\n Default = 'llm-default-session',\n PracticeSession = 'llm-practice-session',\n}\n\ntype DataChannelTokenKey = DataChannelTokenType | string;\n\n/**\n * Latencies (in milliseconds) captured during register + websocket connect.\n */\ntype RegisterAndConnectTiming = {\n clientLLMDatachannelResponseTime?: number;\n clientLLMWebSocketConnectTime?: number;\n};\n\ninterface ILLMChannel {\n registerAndConnect: (\n locusUrl: string,\n datachannelUrl: string,\n datachannelToken?: string,\n sessionId?: string\n ) => Promise<RegisterAndConnectTiming | undefined>;\n isConnected: (sessionId?: string) => boolean;\n getBinding: (sessionId?: string) => string;\n getLocusUrl: (sessionId?: string) => string;\n getDatachannelUrl: (sessionId?: string) => string;\n getWebSocketUrl: (sessionId?: string) => string | undefined;\n disconnectLLM: (\n options: {code: number; reason: string},\n sessionId?: string,\n ownerMeetingId?: string\n ) => Promise<boolean>;\n disconnectAllLLM: (options?: {code: number; reason: string}) => Promise<void>;\n setOwnerMeetingId: (ownerMeetingId: string | undefined, sessionId?: string) => void;\n getOwnerMeetingId: (sessionId?: string) => string | undefined;\n resolveSessionOwnership: (\n ownerMeetingId?: string,\n sessionId?: string\n ) => {\n currentOwner: string | undefined;\n isOwner: boolean;\n };\n getDatachannelToken: (\n tokenKey?: DataChannelTokenKey,\n ownerMeetingId?: string\n ) => string | undefined;\n setDatachannelToken: (\n datachannelToken: string,\n tokenKey?: DataChannelTokenKey,\n ownerMeetingId?: string\n ) => void;\n clearDatachannelToken: (tokenKey: DataChannelTokenKey, ownerMeetingId: string) => void;\n setRefreshHandler: (\n handler: () => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n }>,\n sessionId?: string,\n ownerMeetingId?: string\n ) => void;\n refreshDataChannelToken: (sessionId?: string) => Promise<{\n body: {datachannelToken: string; datachannelTokenType: DataChannelTokenType};\n } | null>;\n getLocusUrlByDatachannelUrl: (requestUrl: string) => string | undefined;\n getSessionIdByDatachannelUrl: (requestUrl: string) => string | undefined;\n getAllConnections: () => Map<\n string,\n {\n webSocketUrl?: string;\n binding?: string;\n locusUrl?: string;\n datachannelUrl?: string;\n ownerMeetingId?: string;\n }\n >;\n}\n\n// eslint-disable-next-line import/prefer-default-export\nexport type {ILLMChannel, DataChannelTokenKey, RegisterAndConnectTiming};\n"],"mappings":";;;;;;;IAAYA,oBAAoB,GAAAC,OAAA,CAAAD,oBAAA,0BAApBA,oBAAoB;EAApBA,oBAAoB;EAApBA,oBAAoB;EAAA,OAApBA,oBAAoB;AAAA;AAOhC;AACA;AACA;AAmEA","ignoreList":[]}
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "node": ">=18"
14
14
  },
15
15
  "dependencies": {
16
- "@webex/internal-plugin-mercury": "3.12.0-next.34"
16
+ "@webex/internal-plugin-mercury": "3.12.0-next.36"
17
17
  },
18
18
  "browserify": {
19
19
  "transform": [
@@ -44,5 +44,5 @@
44
44
  "test:style": "eslint ./src/**/*.*",
45
45
  "test:unit": "webex-legacy-tools test --unit --runner jest"
46
46
  },
47
- "version": "3.12.0-next.37"
47
+ "version": "3.12.0-next.39"
48
48
  }
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@ import * as WebexCore from '@webex/webex-core';
2
2
  import LLMChannel, {config} from './llm';
3
3
  import {DataChannelTokenType} from './llm.types';
4
4
 
5
+ export type {RegisterAndConnectTiming} from './llm.types';
6
+
5
7
  WebexCore.registerInternalPlugin('llm', LLMChannel, {
6
8
  config,
7
9
  });
package/src/llm.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  SUBSCRIPTION_AWARE_SUBCHANNELS_PARAM,
11
11
  LLM_DEFAULT_SESSION,
12
12
  } from './constants';
13
- import {ILLMChannel, DataChannelTokenType} from './llm.types';
13
+ import {ILLMChannel, DataChannelTokenType, RegisterAndConnectTiming} from './llm.types';
14
14
 
15
15
  export const config = {
16
16
  llm: {
@@ -125,7 +125,9 @@ export default class LLMChannel extends (Mercury as any) implements ILLMChannel
125
125
  datachannelUrl: string,
126
126
  datachannelToken?: string,
127
127
  sessionId: string = LLM_DEFAULT_SESSION
128
- ): Promise<void> => {
128
+ ): Promise<RegisterAndConnectTiming | undefined> => {
129
+ const registerStart = performance.now();
130
+
129
131
  // Pre-populate locusUrl and datachannelUrl before register() fires the
130
132
  // HTTP POST, so that any token refresh triggered during registration can
131
133
  // be routed via connections without falling back to a locusInfo URL scan.
@@ -139,18 +141,42 @@ export default class LLMChannel extends (Mercury as any) implements ILLMChannel
139
141
  return this.register(datachannelUrl, datachannelToken, sessionId).then(async () => {
140
142
  if (!locusUrl || !datachannelUrl) return undefined;
141
143
 
144
+ const clientLLMDatachannelResponseTime = Math.round(performance.now() - registerStart);
145
+
142
146
  // locusUrl and datachannelUrl were pre-populated before register(); here
143
147
  // we only need to read the existing session data to get webSocketUrl/binding
144
148
  // that register() filled in.
145
149
  const sessionData = this.connections.get(sessionId) || {};
146
150
 
151
+ if (!sessionData.webSocketUrl) {
152
+ // register() succeeded but the response carried no websocket URL. Attach the measured
153
+ // datachannel time so callers don't misreport a registration that completed as time 0.
154
+ const error: any = new Error(`LLM registration for ${sessionId} returned no websocket URL`);
155
+ error.timing = {clientLLMDatachannelResponseTime};
156
+ throw error;
157
+ }
158
+
147
159
  const isDataChannelTokenEnabled = await this.isDataChannelTokenEnabled();
148
160
 
149
161
  const connectUrl = isDataChannelTokenEnabled
150
162
  ? LLMChannel.buildUrlWithAwareSubchannels(sessionData.webSocketUrl, AWARE_DATA_CHANNEL)
151
163
  : sessionData.webSocketUrl;
152
164
 
153
- return this.connect(connectUrl, sessionId);
165
+ const connectStart = performance.now();
166
+
167
+ try {
168
+ await this.connect(connectUrl, sessionId);
169
+ } catch (error) {
170
+ // register() succeeded; only connect() failed. Attach the measured datachannel time so
171
+ // callers don't misreport a websocket failure as a registration that never completed.
172
+ // @ts-ignore
173
+ error.timing = {clientLLMDatachannelResponseTime};
174
+ throw error;
175
+ }
176
+
177
+ const clientLLMWebSocketConnectTime = Math.round(performance.now() - connectStart);
178
+
179
+ return {clientLLMDatachannelResponseTime, clientLLMWebSocketConnectTime};
154
180
  });
155
181
  };
156
182
 
@@ -198,6 +224,17 @@ export default class LLMChannel extends (Mercury as any) implements ILLMChannel
198
224
  return sessionData?.datachannelUrl;
199
225
  };
200
226
 
227
+ /**
228
+ * Get WebSocket URL for the connection
229
+ * @param {string} sessionId - Connection identifier
230
+ * @returns {string | undefined} WebSocket URL
231
+ */
232
+ public getWebSocketUrl = (sessionId = LLM_DEFAULT_SESSION): string | undefined => {
233
+ const sessionData = this.connections.get(sessionId);
234
+
235
+ return sessionData?.webSocketUrl;
236
+ };
237
+
201
238
  /**
202
239
  * Set the owner meeting ID for a given LLM session. Used by the meetings
203
240
  * plugin to tag which Meeting instance currently owns the (default) LLM
package/src/llm.types.ts CHANGED
@@ -5,17 +5,26 @@ export enum DataChannelTokenType {
5
5
 
6
6
  type DataChannelTokenKey = DataChannelTokenType | string;
7
7
 
8
+ /**
9
+ * Latencies (in milliseconds) captured during register + websocket connect.
10
+ */
11
+ type RegisterAndConnectTiming = {
12
+ clientLLMDatachannelResponseTime?: number;
13
+ clientLLMWebSocketConnectTime?: number;
14
+ };
15
+
8
16
  interface ILLMChannel {
9
17
  registerAndConnect: (
10
18
  locusUrl: string,
11
19
  datachannelUrl: string,
12
20
  datachannelToken?: string,
13
21
  sessionId?: string
14
- ) => Promise<void>;
22
+ ) => Promise<RegisterAndConnectTiming | undefined>;
15
23
  isConnected: (sessionId?: string) => boolean;
16
24
  getBinding: (sessionId?: string) => string;
17
25
  getLocusUrl: (sessionId?: string) => string;
18
26
  getDatachannelUrl: (sessionId?: string) => string;
27
+ getWebSocketUrl: (sessionId?: string) => string | undefined;
19
28
  disconnectLLM: (
20
29
  options: {code: number; reason: string},
21
30
  sessionId?: string,
@@ -66,4 +75,4 @@ interface ILLMChannel {
66
75
  }
67
76
 
68
77
  // eslint-disable-next-line import/prefer-default-export
69
- export type {ILLMChannel, DataChannelTokenKey};
78
+ export type {ILLMChannel, DataChannelTokenKey, RegisterAndConnectTiming};
@@ -173,6 +173,72 @@ describe('plugin-llm', () => {
173
173
 
174
174
  buildSpy.restore();
175
175
  });
176
+
177
+ it('rejects when the registration response has no websocket URL', async () => {
178
+ llmService.connections.set('llm-default-session', {});
179
+ llmService.register = sinon.stub().resolves();
180
+
181
+ await assert.isRejected(
182
+ llmService.registerAndConnect(locusUrl, datachannelUrl),
183
+ /returned no websocket URL/
184
+ );
185
+
186
+ sinon.assert.notCalled(llmService.connect);
187
+ assert.equal(llmService.isConnected(), false);
188
+ });
189
+
190
+ it('attaches the measured datachannel timing to the error when the websocket connect fails', async () => {
191
+ const clock = sinon.useFakeTimers();
192
+
193
+ llmService.isDataChannelTokenEnabled = sinon.stub().resolves(false);
194
+ llmService.register = sinon.stub().callsFake(async () => {
195
+ clock.tick(37);
196
+ const sessionData = llmService.connections.get('llm-default-session') || {};
197
+ sessionData.webSocketUrl = 'wss://example.com/socket';
198
+ sessionData.binding = 'binding';
199
+ llmService.connections.set('llm-default-session', sessionData);
200
+ });
201
+
202
+ const connectError = new Error('websocket connect failed');
203
+ llmService.connect = sinon.stub().rejects(connectError);
204
+
205
+ let caughtError;
206
+ try {
207
+ await llmService.registerAndConnect(locusUrl, datachannelUrl);
208
+ } catch (error) {
209
+ caughtError = error;
210
+ }
211
+
212
+ assert.equal(caughtError, connectError);
213
+ assert.deepEqual(caughtError.timing, {clientLLMDatachannelResponseTime: 37});
214
+ });
215
+
216
+ it('attaches the measured datachannel timing to the error when the response has no websocket URL', async () => {
217
+ const clock = sinon.useFakeTimers();
218
+
219
+ llmService.register = sinon.stub().callsFake(async () => {
220
+ clock.tick(29);
221
+ // register() resolves but the response leaves the session without a websocket URL.
222
+ llmService.connections.set('llm-default-session', {
223
+ locusUrl,
224
+ datachannelUrl,
225
+ });
226
+ });
227
+
228
+ llmService.connect = sinon.stub();
229
+
230
+ let caughtError;
231
+ try {
232
+ await llmService.registerAndConnect(locusUrl, datachannelUrl);
233
+ } catch (error) {
234
+ caughtError = error;
235
+ }
236
+
237
+ sinon.assert.notCalled(llmService.connect);
238
+ assert.instanceOf(caughtError, Error);
239
+ assert.match(caughtError.message, /returned no websocket URL/);
240
+ assert.deepEqual(caughtError.timing, {clientLLMDatachannelResponseTime: 29});
241
+ });
176
242
  });
177
243
 
178
244
  describe('#register', () => {
@@ -709,5 +775,64 @@ describe('plugin-llm', () => {
709
775
  });
710
776
  });
711
777
 
778
+ describe('#registerAndConnect timing', () => {
779
+ it('returns timing data on successful connection', async () => {
780
+ const clock = sinon.useFakeTimers();
781
+
782
+ llmService.isDataChannelTokenEnabled = sinon.stub().resolves(false);
783
+ llmService.register = sinon.stub().callsFake(async () => {
784
+ clock.tick(37);
785
+
786
+ const sessionData = llmService.connections.get('llm-default-session') || {};
787
+
788
+ sessionData.webSocketUrl = 'wss://example.com/socket';
789
+ sessionData.binding = 'binding';
790
+ llmService.connections.set('llm-default-session', sessionData);
791
+ });
792
+ llmService.connect = sinon.stub().callsFake(async () => {
793
+ clock.tick(23);
794
+ });
795
+
796
+ const result = await llmService.registerAndConnect(locusUrl, datachannelUrl, undefined);
797
+
798
+ assert.deepEqual(result, {
799
+ clientLLMDatachannelResponseTime: 37,
800
+ clientLLMWebSocketConnectTime: 23,
801
+ });
802
+ });
803
+
804
+ it('returns undefined when locusUrl is empty', async () => {
805
+ llmService.register = sinon.stub().resolves();
806
+
807
+ const result = await llmService.registerAndConnect('', datachannelUrl, undefined);
808
+
809
+ assert.isUndefined(result);
810
+ });
811
+ });
812
+
813
+ describe('#getWebSocketUrl', () => {
814
+ it('returns the websocket URL for default session', () => {
815
+ llmService.connections.set('llm-default-session', {
816
+ webSocketUrl: 'wss://test.example.com/ws',
817
+ });
818
+
819
+ assert.equal(llmService.getWebSocketUrl(), 'wss://test.example.com/ws');
820
+ });
821
+
822
+ it('returns undefined when no connection exists', () => {
823
+ llmService.connections.clear();
824
+
825
+ assert.isUndefined(llmService.getWebSocketUrl());
826
+ });
827
+
828
+ it('returns the websocket URL for custom session', () => {
829
+ llmService.connections.set('custom-session', {
830
+ webSocketUrl: 'wss://custom.example.com/ws',
831
+ });
832
+
833
+ assert.equal(llmService.getWebSocketUrl('custom-session'), 'wss://custom.example.com/ws');
834
+ });
835
+ });
836
+
712
837
  });
713
838
  });