@webex/contact-center 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.
Files changed (74) hide show
  1. package/.sdd/manifest.json +4 -3
  2. package/ai-docs/CONTRACTS.md +4 -3
  3. package/dist/cc.js +108 -6
  4. package/dist/cc.js.map +1 -1
  5. package/dist/metrics/MetricsManager.js +16 -0
  6. package/dist/metrics/MetricsManager.js.map +1 -1
  7. package/dist/metrics/behavioral-events.js +91 -0
  8. package/dist/metrics/behavioral-events.js.map +1 -1
  9. package/dist/metrics/constants.js +16 -0
  10. package/dist/metrics/constants.js.map +1 -1
  11. package/dist/services/AnswerCallOnWebexService.js +34 -6
  12. package/dist/services/AnswerCallOnWebexService.js.map +1 -1
  13. package/dist/services/WebexCrossClientService.js +54 -3
  14. package/dist/services/WebexCrossClientService.js.map +1 -1
  15. package/dist/services/WxAppTelephonyMercurySync.js +21 -6
  16. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
  17. package/dist/services/task/TaskManager.js +137 -0
  18. package/dist/services/task/TaskManager.js.map +1 -1
  19. package/dist/services/task/state-machine/TaskStateMachine.js +51 -40
  20. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  21. package/dist/services/task/state-machine/guards.js +7 -37
  22. package/dist/services/task/state-machine/guards.js.map +1 -1
  23. package/dist/services/task/voice/Voice.js +120 -15
  24. package/dist/services/task/voice/Voice.js.map +1 -1
  25. package/dist/services/task/voice/wxAppVoiceMethods.js +154 -1
  26. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
  27. package/dist/services/wxAppDiagnosticLogging.js +122 -0
  28. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  29. package/dist/types/cc.d.ts +1 -0
  30. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  31. package/dist/types/metrics/constants.d.ts +15 -0
  32. package/dist/types/services/AnswerCallOnWebexService.d.ts +1 -0
  33. package/dist/types/services/WebexCrossClientService.d.ts +4 -0
  34. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +86 -42
  35. package/dist/types/services/task/state-machine/guards.d.ts +4 -4
  36. package/dist/types/services/task/voice/Voice.d.ts +5 -1
  37. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +3 -0
  38. package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
  39. package/dist/webex.js +1 -1
  40. package/package.json +9 -9
  41. package/src/cc.ts +155 -7
  42. package/src/metrics/MetricsManager.ts +16 -0
  43. package/src/metrics/ai-docs/metrics-spec.md +71 -9
  44. package/src/metrics/behavioral-events.ts +92 -0
  45. package/src/metrics/constants.ts +18 -0
  46. package/src/services/AnswerCallOnWebexService.ts +41 -8
  47. package/src/services/WebexCrossClientService.ts +85 -4
  48. package/src/services/WxAppTelephonyMercurySync.ts +31 -4
  49. package/src/services/agent/ai-docs/agent-spec.md +1 -0
  50. package/src/services/task/TaskManager.ts +194 -1
  51. package/src/services/task/ai-docs/task-spec.md +20 -17
  52. package/src/services/task/state-machine/TaskStateMachine.ts +47 -40
  53. package/src/services/task/state-machine/ai-docs/AGENTS.md +2 -4
  54. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +26 -16
  55. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +54 -16
  56. package/src/services/task/state-machine/guards.ts +7 -63
  57. package/src/services/task/voice/Voice.ts +159 -28
  58. package/src/services/task/voice/wxAppVoiceMethods.ts +211 -2
  59. package/src/services/wxAppDiagnosticLogging.ts +174 -0
  60. package/test/unit/spec/cc.ts +251 -22
  61. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  62. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  63. package/test/unit/spec/services/AnswerCallOnWebexService.ts +41 -0
  64. package/test/unit/spec/services/WebexCrossClientService.ts +207 -1
  65. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +23 -0
  66. package/test/unit/spec/services/task/Task.ts +74 -6
  67. package/test/unit/spec/services/task/TaskManager.ts +416 -1
  68. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +640 -46
  69. package/test/unit/spec/services/task/state-machine/guards.ts +6 -6
  70. package/test/unit/spec/services/task/voice/Voice.ts +538 -0
  71. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +145 -1
  72. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
  73. package/umd/contact-center.min.js +2 -2
  74. package/umd/contact-center.min.js.map +1 -1
@@ -52,7 +52,8 @@
52
52
  "contracts": {
53
53
  "provides": [
54
54
  "Published @webex/contact-center package; ContactCenter WebexPlugin; public SDK methods, events, types, Task with ordered action-specific consult/transfer destination controls, AddressBook, ApiAIAssistant, UserPreference, cc.userPreference, existing getQueues/getEntryPoints methods with consult/transfer defaults, full Queue records, and mapped EntryPoint records with optional numbers, exported user-preference request/response types, and preview-campaign methods acceptPreviewContact/skipPreviewContact/removePreviewContact",
55
- "WXCC-6026 wxApp Better Together: enableWxBetterTogether init config, isWxBetterTogetherEnabled read API, unified task telephony (task.accept/decline/toggleMute/transmitDtmf), TASK_WXAPP_MUTE_STATE_UPDATED event, optional main.keypad uiControl, multi-login supported; runtime toggle deferred to Phase 2 (private)"
55
+ "WXCC-6026 wxApp Better Together: enableWxBetterTogether init config, isWxBetterTogetherEnabled read API, unified task telephony (task.accept/decline/toggleMute/transmitDtmf), TASK_WXAPP_MUTE_STATE_UPDATED event, optional main.keypad uiControl, multi-login supported; runtime toggle deferred to Phase 2 (private)",
56
+ "BROWSER stationLogin payload: dialNumber and deviceId are webrtc-{agentId} (contact-center.station-login-browser-dn / AGENT-R-006); public stationLogin({loginOption: 'BROWSER'}) input unchanged"
56
57
  ],
57
58
  "requires": [
58
59
  "Webex SDK host/plugin runtime; Contact Center REST and WebSocket services; internal Webex calling, metrics, support, authorization, logger, and mercury packages"
@@ -180,7 +181,7 @@
180
181
  "canonical_spec": "src/services/agent/ai-docs/agent-spec.md",
181
182
  "contracts": {
182
183
  "provides": [
183
- "Agent request factory; station login/logout, state changes, buddy-agent and relogin operations; AGENT_EVENTS and agent types"
184
+ "Agent request factory; station login/logout, state changes, buddy-agent and relogin operations; AGENT_EVENTS and agent types; BROWSER station login sends dialNumber and deviceId as webrtc-{agentId} (contact-center.station-login-browser-dn / AGENT-R-006)"
184
185
  ],
185
186
  "requires": [
186
187
  "AqmReqs; WCC API gateway; correlated WebSocket notifications; ContactCenter registration/profile state"
@@ -309,7 +310,7 @@
309
310
  "canonical_spec": "src/services/task/ai-docs/task-spec.md",
310
311
  "contracts": {
311
312
  "provides": [
312
- "Task/Voice/WebRTC/Digital lifecycle APIs; TaskManager lifecycle correlation, narrowly scoped backend-authoritative ContactOwnerChanged recovery, and task-event publication, including routing owner-changing ContactUpdated through the existing task:hydrate event; task events/types; ordered action-specific consult/transfer destination controls; call-control and dialer operations including acceptPreviewContact, skipPreviewContact, and removePreviewContact"
313
+ "Task/Voice/WebRTC/Digital lifecycle APIs; TaskManager lifecycle correlation, narrowly scoped backend-authoritative ContactOwnerChanged recovery, and task-event publication, including routing owner-changing ContactUpdated through the existing task:hydrate event; TaskManager/Voice stamp wrapUpRequired on wrap-up ingress so task:wrapup and profile-gated auto wrap-up can start; task events/types; ordered action-specific consult/transfer destination controls; call-control and dialer operations including acceptPreviewContact, skipPreviewContact, and removePreviewContact"
313
314
  ],
314
315
  "requires": [
315
316
  "Contact/dialer AqmReqs; WebSocket managers; @webex/calling; MetricsManager; XState task-state module"
@@ -6,7 +6,8 @@
6
6
  | Contract ID | Owner module/package | Symbol | Signature / shape | Stability / deprecation | Schema / detail link | Defined at |
7
7
  |---|---|---|---|---|---|---|
8
8
  | `contact-center.plugin` | Contact Center | `ContactCenter` / default plugin | WebexPlugin implementing `IContactCenter` | semver public | `ai-docs/contact-center-spec.md` | `src/index.ts` |
9
- | `contact-center.routing-agent` | Agent/Core | `routingAgent` | `(routing: AqmReqs) => {stationLogin, logout, stateChange, buddyAgents, reload}` | semver public export; returned methods follow backend contracts | `src/services/agent/ai-docs/agent-spec.md` | `src/index.ts`, `src/services/agent/index.ts` |
9
+ | `contact-center.routing-agent` | Agent/Core | `routingAgent` | `(routing: AqmReqs) => {stationLogin, logout, stateChange, buddyAgents, reload}`; BROWSER login payload follows `contact-center.station-login-browser-dn` | semver public export; returned methods follow backend contracts | `src/services/agent/ai-docs/agent-spec.md` | `src/index.ts`, `src/services/agent/index.ts` |
10
+ | `contact-center.station-login-browser-dn` | Contact Center / Agent | `cc.stationLogin` BROWSER payload on `POST /v1/agents/login` | BROWSER sends `dialNumber` and `deviceId` as `webrtc-{agentId}`; EXTENSION and AGENT_DN continue to send the caller-supplied dial number for both fields | backend contract alignment; public `stationLogin({loginOption: 'BROWSER'})` input unchanged | `src/services/agent/ai-docs/agent-spec.md` AGENT-R-006 | `src/cc.ts` |
10
11
  | `contact-center.task` | Task | `Task`, `ITask`, task payload/control types | task instance and compatibility interface | semver public | `src/services/task/ai-docs/task-spec.md` | `src/index.ts` |
11
12
  | `contact-center.events` | Agent/Task/Config | `AGENT_EVENTS`, `TASK_EVENTS`, `CC_AGENT_EVENTS`, `CC_TASK_EVENTS`, `CC_EVENTS` | typed string event contracts | additive; removals or semantic changes are breaking | owning module specs | `src/index.ts` |
12
13
  | `contact-center.data` | Services/Utils | `AddressBook`, `ApiAIAssistant`, SDK data response/search types | SDK data and AI-assistant access | semver public | Services/Utils specs | `src/index.ts` |
@@ -14,7 +15,7 @@
14
15
  | `contact-center.user-preference` | Services/Config | `UserPreference`, `cc.userPreference`, `UserPreferenceData`, request/response types | `getUserPreference(params?)`, `createUserPreference(data)`, `updateUserPreference(userId, data)`, `deleteUserPreference(userId)` | semver public | `src/services/ai-docs/services-spec.md` | `src/index.ts`, `src/cc.ts`, `src/services/UserPreference.ts`, `src/services/config/types.ts` |
15
16
  | `contact-center.preview-campaign` | Contact Center/Task | `acceptPreviewContact`, `skipPreviewContact`, `removePreviewContact` | `(payload: PreviewContactPayload) => Promise<TaskResponse>` | semver public | `ai-docs/contact-center-spec.md`, `src/services/task/ai-docs/task-spec.md` | `src/cc.ts`, `src/services/task/dialer.ts`, `src/services/task/types.ts` |
16
17
  | `contact-center.state-controls` | Task state machine | `getDefaultUIControls`, `TaskUIControls` with ordered `consultTransferDestinations` arrays | Task controls include per-leg action state plus ordered `consult`/`transfer` destination categories derived from profile and interaction policy | additive semver public field; destination arrays are SDK-owned and first item is the consumer default | `src/services/task/state-machine/ai-docs/task-state-machine-spec.md` | `src/index.ts`, `src/services/task/types.ts`, `src/services/task/state-machine/uiControlsComputer.ts` |
17
- | `contact-center.conference-participant-drop` | Task | `DropConferenceParticipantPayload`, `ITask.dropConferenceParticipant` | `(payload: {participantId: string}) => Promise<TaskResponse>`; Voice resolves from correlated `ParticipantLeftConference`, while non-voice base Task rejects as unsupported. Existing participant/consult lifecycle events may resolve one unique child-keyed task by `mainInteractionId`; no new public event is introduced. | semver public | `src/services/task/ai-docs/task-spec.md` | `src/index.ts`, `src/services/task/types.ts`, `src/services/task/voice/Voice.ts`, `src/services/task/contact.ts`, `src/services/task/TaskManager.ts` |
18
+ | `contact-center.conference-participant-drop` | Task | `DropConferenceParticipantPayload`, `ITask.dropConferenceParticipant` | `(payload: {participantId: string}) => Promise<TaskResponse>`; Voice resolves from correlated `ParticipantLeftConference`, while non-voice base Task rejects as unsupported. Existing participant/consult lifecycle events may resolve one unique child-keyed task by `mainInteractionId`; no new public event is introduced. | semver public | `src/services/task/ai-docs/task-spec.md`, `src/services/task/state-machine/ai-docs/task-state-machine-spec.md` | `src/index.ts`, `src/services/task/types.ts`, `src/services/task/voice/Voice.ts`, `src/services/task/contact.ts`, `src/services/task/TaskManager.ts` |
18
19
  | `contact-center.wxapp-answer` | Contact Center / Task | `enableWxBetterTogether`, `isWxBetterTogetherEnabled()`, `getWxAppMuted`, `syncWxAppMuteFromCallDetails`, unified task telephony (`ITask.accept`, `decline`, `toggleMute({ muted?, lineOwnerId? })`, `transmitDtmf({ dtmf, lineOwnerId? })`) | init flag ON → usersub `true` + Mercury on supported station login **and silent relogin**; init flag OFF → force usersub `false` on supported station login **and silent relogin** (clears stale suppression after refresh); **Phase 1 init-only** — change flag via re-init; multi-login supported; mute backfill via GET call details + `getWxAppMuted()` on hydrate/refresh; shared-line `lineOwnerId` defaults from participant; SDK `Voice` routes wxApp telephony when flag active | semver public | `src/services/task/ai-docs/task-spec.md`, `ai-docs/contact-center-spec.md`, `src/services/ai-docs/services-spec.md` | `src/cc.ts`, `src/services/task/voice/Voice.ts`, `src/services/task/voice/wxAppVoiceMethods.ts` |
19
20
 
20
21
  ### Events
@@ -22,7 +23,7 @@
22
23
  | Contract ID | Owner module | Event / topic | Direction | Payload schema link | Delivery guarantees | Compatibility / deprecation | Defined at |
23
24
  |---|---|---|---|---|---|---|---|
24
25
  | `agent.events` | Agent | `agent:*` | publish to application | agent spec/types | realtime; backend ordering/correlation | additive constants | `src/services/agent/types.ts` |
25
- | `task.events` | Task | `task:*`, including existing `task:hydrate` | consume/map backend task lifecycle events, then publish to application/task | task spec `TASK-R-010` / types | `ContactOwnerChanged` and owner-changing `ContactUpdated` route through `CONTACT_OWNER_CHANGED`; a complete promoted-current-Agent owner change may narrowly recover a missing task; consumers receive one `task:hydrate` and no synthetic `task:incoming` | compatible semantic extension of an existing event; no new event constant; backend `interaction.owner` remains authoritative | `src/services/task/TaskManager.ts`, `src/services/task/Task.ts`, `src/services/task/types.ts` |
26
+ | `task.events` | Task | `task:*`, including existing `task:hydrate` and `task:wrapup` | consume/map backend task lifecycle events, then publish to application/task | task spec `TASK-R-010` / `TASK-R-011` / types | `ContactOwnerChanged` and owner-changing `ContactUpdated` route through `CONTACT_OWNER_CHANGED`; a complete promoted-current-Agent owner change may narrowly recover a missing task; consumers receive one `task:hydrate` and no synthetic `task:incoming`. TaskManager stamps `wrapUpRequired` once at ingress (`AGENT_WRAPUP`, conference-end/leave/transfer when this agent should wrap; HTTP `exitConference` on Voice uses the same empty-pending self-exit fallback as leave — true unless this agent is a consulted non-owner — and pending-list precedence uses the correlated response only) so consumers may receive `task:wrapup` and `task.autoWrapup` on those events. `PARTICIPANT_LEAVE` into `WRAPPING_UP` does not transition-emit; the `WRAPPING_UP` entry still emits `task:wrapup`. A nonempty `agentsPendingWrapUp` wins over transfer initiator signals (`consultingAgentId` / `transferConferenceRequested`); a named other `participantId` is never self-leave; leave wrap-up is not stamped when the actor ignores `PARTICIPANT_LEAVE` (`CONNECTED`, `HOLD_INITIATING`, `CONF_INITIATING`); leave-self with empty/absent pending stamps true unless this agent is a consulted non-owner; remaining conference agents stay unstamped. Profile-gated auto wrap-up still requires `autoWrapup !== false`, interval, and a default reason | compatible semantic extension of existing events; no new event constant; backend `interaction.owner` remains authoritative | `src/services/task/TaskManager.ts`, `src/services/task/Task.ts`, `src/services/task/voice/Voice.ts`, `src/services/task/types.ts` |
26
27
  | `task.wxapp-mute.events` | Task | `task:wxapp-mute-state-updated` | publish to application/task | task spec/types | event-driven | additive | `src/services/task/types.ts`, `src/services/task/voice/Voice.ts` |
27
28
  | `cc.events` | Config/Core | backend CC_EVENTS | consume/map | config/core specs | remote WebSocket delivery; AQM correlation where configured | backend contract | `src/services/config/types.ts` |
28
29
  | `rtd.events` | Task | realtime transcription/suggestion | consume then publish per task | task spec | websocket best-effort according to remote service | additive payloads | `src/services/task/TaskManager.ts` |
package/dist/cc.js CHANGED
@@ -22,6 +22,7 @@ var _WebCallingService = _interopRequireDefault(require("./services/WebCallingSe
22
22
  var _AnswerCallOnWebexService = _interopRequireDefault(require("./services/AnswerCallOnWebexService"));
23
23
  var _WebexCrossClientService = _interopRequireDefault(require("./services/WebexCrossClientService"));
24
24
  var _WxAppTelephonyMercurySync = _interopRequireDefault(require("./services/WxAppTelephonyMercurySync"));
25
+ var _wxAppDiagnosticLogging = require("./services/wxAppDiagnosticLogging");
25
26
  var _types4 = require("./services/task/types");
26
27
  var _MetricsManager = _interopRequireDefault(require("./metrics/MetricsManager"));
27
28
  var _constants4 = require("./metrics/constants");
@@ -861,13 +862,15 @@ class ContactCenter extends _webexCore.WebexPlugin {
861
862
  throw error;
862
863
  }
863
864
  }
865
+ const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
864
866
  const loginResponse = await this.services.agent.stationLogin({
865
867
  data: {
866
- dialNumber: data.loginOption === _types.LoginOption.BROWSER ? this.agentConfig.agentId : data.dialNumber,
868
+ // BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
869
+ dialNumber: data.loginOption === _types.LoginOption.BROWSER ? deviceId : data.dialNumber,
867
870
  teamId: data.teamId,
868
871
  deviceType: data.loginOption,
869
872
  isExtension: data.loginOption === _types.LoginOption.EXTENSION,
870
- deviceId: this.getDeviceId(data.loginOption, data.dialNumber),
873
+ deviceId,
871
874
  roles: [_constants3.AGENT],
872
875
  teamName: _constants.EMPTY_STRING,
873
876
  siteId: _constants.EMPTY_STRING,
@@ -1337,17 +1340,72 @@ class ContactCenter extends _webexCore.WebexPlugin {
1337
1340
  throw error;
1338
1341
  }
1339
1342
  }
1343
+ getWxAppTelephonyTaskType() {
1344
+ const loginOption = this.getCurrentStationLoginOption();
1345
+ if (loginOption === _types.LoginOption.BROWSER) {
1346
+ return 'WebRTC';
1347
+ }
1348
+ if (loginOption === _types.LoginOption.EXTENSION || loginOption === _types.LoginOption.AGENT_DN) {
1349
+ return 'Voice';
1350
+ }
1351
+ return 'unknown';
1352
+ }
1340
1353
  async ensureWxAppPostStationLogin() {
1341
- if (this.getCurrentStationLoginOption() === _types.LoginOption.BROWSER) {
1354
+ const loginOption = this.getCurrentStationLoginOption();
1355
+ const flagEnabled = this.isWxBetterTogetherEnabled();
1356
+ const telephonyTaskType = this.getWxAppTelephonyTaskType();
1357
+ if (loginOption === _types.LoginOption.BROWSER) {
1358
+ if (flagEnabled) {
1359
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1360
+ enableWxBetterTogether: true,
1361
+ loginOption,
1362
+ wxAppHooksApplied: false,
1363
+ usersubPublished: false,
1364
+ mercurySubscribed: false,
1365
+ telephonyTaskType,
1366
+ skipReason: 'unsupported_browser_login'
1367
+ });
1368
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED, {
1369
+ loginOption,
1370
+ enableWxBetterTogether: true,
1371
+ skipReason: 'unsupported_browser_login'
1372
+ }, ['operational', 'behavioral']);
1373
+ }
1342
1374
  return;
1343
1375
  }
1344
1376
  let publishedEnable = false;
1377
+ let wxAppInitStage = 'mercury';
1345
1378
  try {
1346
- if (this.isWxBetterTogetherEnabled()) {
1379
+ if (flagEnabled) {
1380
+ this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS, _constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED]);
1347
1381
  await this.ensureWxAppMercuryAndSubscribe();
1382
+ wxAppInitStage = 'publish';
1348
1383
  await this.publishAnswerOnWebexCrossClientState(true);
1349
1384
  publishedEnable = true;
1350
1385
  this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1386
+ const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
1387
+ const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
1388
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1389
+ enableWxBetterTogether: true,
1390
+ loginOption,
1391
+ wxAppHooksApplied: true,
1392
+ usersubPublished,
1393
+ mercurySubscribed,
1394
+ telephonyTaskType
1395
+ });
1396
+ const sessionInitReady = mercurySubscribed && usersubPublished;
1397
+ if (sessionInitReady) {
1398
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS, {
1399
+ loginOption,
1400
+ enableWxBetterTogether: true
1401
+ }, ['operational', 'behavioral']);
1402
+ } else {
1403
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED, {
1404
+ loginOption,
1405
+ enableWxBetterTogether: true,
1406
+ skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed'
1407
+ }, ['operational', 'behavioral']);
1408
+ }
1351
1409
  } else {
1352
1410
  await this.ensureWxAppDeviceRegistered();
1353
1411
  await this.publishAnswerOnWebexCrossClientState(false, {
@@ -1356,13 +1414,43 @@ class ContactCenter extends _webexCore.WebexPlugin {
1356
1414
  this.webexCrossClientService.teardown();
1357
1415
  this.wxAppTelephonyMercurySync.unsubscribe();
1358
1416
  await this.releaseWxAppMercuryResources();
1417
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1418
+ enableWxBetterTogether: false,
1419
+ loginOption,
1420
+ wxAppHooksApplied: true,
1421
+ usersubPublished: false,
1422
+ mercurySubscribed: false,
1423
+ telephonyTaskType,
1424
+ skipReason: 'flag_disabled'
1425
+ });
1426
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED, {
1427
+ loginOption,
1428
+ enableWxBetterTogether: false,
1429
+ skipReason: 'flag_disabled'
1430
+ }, ['operational', 'behavioral']);
1359
1431
  }
1360
1432
  } catch (error) {
1361
1433
  _loggerProxy.default.error(`Failed to initialize wxApp post-station-login: ${error}`, {
1362
1434
  module: _constants.CC_FILE,
1363
1435
  method: _constants.METHODS.STATION_LOGIN
1364
1436
  });
1365
- if (this.isWxBetterTogetherEnabled()) {
1437
+ if (flagEnabled) {
1438
+ const sessionInitFailureReason = wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
1439
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED, {
1440
+ loginOption,
1441
+ enableWxBetterTogether: true,
1442
+ skipReason: sessionInitFailureReason,
1443
+ error: error instanceof Error ? error.toString() : String(error)
1444
+ }, ['operational', 'behavioral']);
1445
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1446
+ enableWxBetterTogether: true,
1447
+ loginOption,
1448
+ wxAppHooksApplied: false,
1449
+ usersubPublished: publishedEnable && this.webexCrossClientService.isAnswerCallsStateActive(),
1450
+ mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
1451
+ telephonyTaskType,
1452
+ skipReason: sessionInitFailureReason
1453
+ });
1366
1454
  if (publishedEnable) {
1367
1455
  await this.revertWxAppCrossClientPublish();
1368
1456
  } else {
@@ -1563,6 +1651,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
1563
1651
  module: _constants.CC_FILE,
1564
1652
  method: _constants.METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY
1565
1653
  });
1654
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED, {
1655
+ error: 'agentId unavailable'
1656
+ }, ['operational', 'behavioral']);
1566
1657
  return;
1567
1658
  }
1568
1659
  try {
@@ -1575,6 +1666,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
1575
1666
  module: _constants.CC_FILE,
1576
1667
  method: _constants.METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY
1577
1668
  });
1669
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED, {
1670
+ error: error instanceof Error ? error.toString() : String(error)
1671
+ }, ['operational', 'behavioral']);
1578
1672
  this.wxAppTelephonyMercurySync.unsubscribe();
1579
1673
  await this.releaseWxAppMercuryResources();
1580
1674
  throw error;
@@ -1589,10 +1683,18 @@ class ContactCenter extends _webexCore.WebexPlugin {
1589
1683
  }
1590
1684
  const userId = this.$webex.internal.device?.userId;
1591
1685
  if (!userId) {
1686
+ if (enable && this.isWxBetterTogetherEnabled()) {
1687
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED, {
1688
+ enableWxBetterTogether: true,
1689
+ skipReason: 'user_id_unavailable',
1690
+ error: 'User ID is unavailable for cross-client publish'
1691
+ }, ['operational', 'behavioral']);
1692
+ }
1592
1693
  return;
1593
1694
  }
1594
1695
  await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
1595
- userId
1696
+ userId,
1697
+ trackPublishMetrics: true
1596
1698
  });
1597
1699
  if (enable) {
1598
1700
  this.clearWxAppFalsePublishRetryTimer();