@webex/contact-center 3.12.0-llmrefactor.12 → 3.12.0-llmrefactor.14
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/.sdd/manifest.json +6 -5
- package/ai-docs/CONTRACTS.md +8 -0
- package/ai-docs/SERVICE_STATE.md +4 -0
- package/ai-docs/contact-center-spec.md +26 -3
- package/ai-docs/features/contact-owner-change-refactor.md +78 -0
- package/dist/cc.js +202 -9
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +25 -10
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +6 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +6 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +4 -4
- package/dist/services/AnswerCallOnWebexService.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +116 -1
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/WebexCrossClientService.js +5 -2
- package/dist/services/WebexCrossClientService.js.map +1 -1
- package/dist/services/WxAppTelephonyMercurySync.js +6 -3
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
- package/dist/services/config/Util.js +5 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +10 -1
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +51 -0
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +4 -3
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Utils.js +5 -1
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/task/TaskFactory.js +1 -0
- package/dist/services/task/TaskFactory.js.map +1 -1
- package/dist/services/task/TaskManager.js +50 -58
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +63 -44
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
- package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js +52 -11
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
- package/dist/services/wxAppDiagnosticLogging.js +50 -13
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -1
- package/dist/types/cc.d.ts +21 -1
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/index.d.ts +11 -1
- package/dist/types/metrics/constants.d.ts +6 -0
- package/dist/types/services/ApiAiAssistant.d.ts +35 -2
- package/dist/types/services/config/constants.d.ts +5 -0
- package/dist/types/services/config/types.d.ts +15 -2
- package/dist/types/services/core/Utils.d.ts +2 -0
- package/dist/types/services/task/types.d.ts +2 -0
- package/dist/types/services/task/voice/Voice.d.ts +15 -2
- package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +19 -6
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +12 -0
- package/dist/types/types.d.ts +56 -1
- package/dist/types.js +39 -2
- package/dist/types.js.map +1 -1
- package/dist/webex.js +9 -1
- package/dist/webex.js.map +1 -1
- package/package.json +9 -9
- package/src/cc.ts +294 -25
- package/src/constants.ts +4 -0
- package/src/index.ts +11 -0
- package/src/metrics/ai-docs/metrics-spec.md +61 -20
- package/src/metrics/behavioral-events.ts +6 -0
- package/src/metrics/constants.ts +6 -0
- package/src/services/AnswerCallOnWebexService.ts +4 -4
- package/src/services/ApiAiAssistant.ts +153 -1
- package/src/services/WebexCrossClientService.ts +7 -2
- package/src/services/WxAppTelephonyMercurySync.ts +3 -3
- package/src/services/ai-docs/services-spec.md +11 -3
- package/src/services/config/Util.ts +6 -0
- package/src/services/config/ai-docs/config-spec.md +14 -3
- package/src/services/config/constants.ts +12 -0
- package/src/services/config/index.ts +65 -0
- package/src/services/config/types.ts +13 -2
- package/src/services/core/Utils.ts +4 -0
- package/src/services/task/TaskFactory.ts +1 -0
- package/src/services/task/TaskManager.ts +85 -87
- package/src/services/task/ai-docs/task-spec.md +7 -3
- package/src/services/task/types.ts +16 -0
- package/src/services/task/voice/Voice.ts +74 -52
- package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +83 -24
- package/src/services/wxAppDiagnosticLogging.ts +135 -23
- package/src/types.ts +65 -0
- package/src/webex.js +8 -0
- package/test/unit/spec/cc.ts +469 -19
- package/test/unit/spec/services/ApiAiAssistant.ts +134 -2
- package/test/unit/spec/services/WebexCrossClientService.ts +45 -3
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +1 -1
- package/test/unit/spec/services/config/Util.ts +44 -0
- package/test/unit/spec/services/config/index.ts +68 -0
- package/test/unit/spec/services/task/TaskManager.ts +214 -3
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +33 -0
- package/test/unit/spec/services/task/voice/Voice.ts +10 -51
- package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +121 -5
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +44 -8
- package/test/unit/spec/wellnessSampleUtils.ts +288 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
| Doc kind | Module spec |
|
|
12
12
|
| Coverage score | Partial (manifest-authoritative); 15/15 required document fields present |
|
|
13
13
|
| Generated from | `module-spec` @ SDLC template library `0.2.1` |
|
|
14
|
-
| generated_by / approved_by / updated_at | Codex generator / developer-approved
|
|
15
|
-
| Validation status |
|
|
14
|
+
| generated_by / approved_by / updated_at | Codex generator / developer-approved Agent Wellness Break delta / 2026-09-02 |
|
|
15
|
+
| Validation status | Agent Wellness Break v0.4 delta independently validated by claude-code on 2026-09-07; coverage remains Partial until the remaining baseline promotion criteria are satisfied |
|
|
16
16
|
|
|
17
17
|
## Evidence Rules
|
|
18
18
|
Every requirement cites stable source and test file paths. Code/tests are the behavioral referee; routed source text supplies explicit intent and rationale. Missing or contradictory evidence blocks promotion.
|
|
@@ -100,6 +100,8 @@ services/config/
|
|
|
100
100
|
| Organization masking | `OrgSettings.maskSensitiveData` | `src/services/config/types.ts`, `src/services/config/Util.ts` |
|
|
101
101
|
| Auxiliary-code list URL | includes `desktopProfileFilter=true` | `src/services/config/constants.ts` |
|
|
102
102
|
| Outdial ANI entries | public wrapper through ContactCenter | `src/services/config/index.ts`, `src/cc.ts` |
|
|
103
|
+
| `Profile.isWellnessBreakEnabled` | required effective flag derived from backend AI feature configuration and organization license quantity | `src/services/config/Util.ts`, `src/services/config/types.ts` |
|
|
104
|
+
| `getWellbeingBreakIdleCode(orgId)` | paginated exact lookup for the active system idle code named `WellbeingBreak`, exposed and registration-cached by ContactCenter | `src/services/config/index.ts`, `src/services/config/constants.ts`, `src/cc.ts` |
|
|
103
105
|
|
|
104
106
|
The service has no `TeamList.channelMap` contract. Exact package exports are indexed in root `CONTRACTS.md`.
|
|
105
107
|
|
|
@@ -116,6 +118,8 @@ The service has no `TeamList.channelMap` contract. Exact package exports are ind
|
|
|
116
118
|
| CONFIG-R-003 | Paginate teams and auxiliary codes until completion and include `desktopProfileFilter=true` for auxiliary-code requests. | A partial or unfiltered set yields invalid profile/team/auxiliary choices. | `src/services/config/constants.ts` | `test/unit/spec/services/config/index.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
117
119
|
| CONFIG-R-004 | Expose current response/field names: `MultimediaProfileResponse`, `OrgSettings.maskSensitiveData`, and real TeamList fields only. | Type-name drift causes invalid consumer code and incorrect privacy behavior. | `src/services/config/types.ts` | `test/unit/spec/services/config/index.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
118
120
|
| CONFIG-R-005 | Reject the entire profile aggregation when any required dependent request fails. | Consumers must never receive an internally inconsistent partial Profile. | `src/services/config/index.ts` | `test/unit/spec/services/config/index.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
121
|
+
| CONFIG-R-006 | Always populate `Profile.isWellnessBreakEnabled`; set it true only when `agentWellbeing.enable` is true, `wellnessBreakReminders` is `ENABLED`, and `OrgSettings.aiAssistantQuantity` is greater than zero. Missing or null inputs fail closed. | Hosts need one license-and-server-rollout-aware signal rather than reconstructing partial configuration. | `src/services/config/Util.ts`, `src/services/config/types.ts` | `test/unit/spec/services/config/Util.ts` | The supported SDK contracts expose no separate rollout response field; backend delivery of enabled `agentWellbeing` configuration is the rollout decision. | PRESENT |
|
|
122
|
+
| CONFIG-R-007 | Retrieve the wellness idle code from `/v2/auxiliary-code` using `workType=IDLE_CODE`, `customFilter=isSystemCode==true`, and `desktopProfileFilter=false`; follow pagination and accept only the exact active system code name `WellbeingBreak`. | The dedicated state must be independent of desktop-profile filtering and must never fall back to a similarly named or non-system code. | `src/services/config/index.ts`, `src/services/config/constants.ts` | `test/unit/spec/services/config/index.ts` | None. | PRESENT |
|
|
119
123
|
|
|
120
124
|
## Design Overview
|
|
121
125
|
Config separates its stable consumption boundary from collaborators so ownership and failure behavior stay explicit. Profile creation is all-or-nothing across dependent API calls so consumers never receive internally inconsistent partial configuration.
|
|
@@ -162,6 +166,7 @@ The AgentProfile is defined as the [`Profile`](../types.ts) type. This is not an
|
|
|
162
166
|
| `accessQueue` | `'ALL' \| 'SPECIFIC' \| 'NONE'` | Collaboration tab queue access scope from Desktop Profile |
|
|
163
167
|
| `accessEntryPoint` | `'ALL' \| 'SPECIFIC' \| 'NONE'` | Collaboration tab entry-point access scope from Desktop Profile |
|
|
164
168
|
| `accessBuddyTeam` | `'ALL' \| 'SPECIFIC' \| 'NONE'` | Collaboration tab buddy-team access scope from Desktop Profile |
|
|
169
|
+
| `isWellnessBreakEnabled` | boolean | Effective wellness gate derived from backend enablement/reminder configuration and positive AI Assistant license quantity |
|
|
165
170
|
|
|
166
171
|
`parseAgentConfigs()` maps the three `access*` fields from `DesktopProfileResponse` onto the public `Profile` returned by `cc.register()`.
|
|
167
172
|
|
|
@@ -264,7 +269,7 @@ The service fetches data from multiple APIs with these response structures:
|
|
|
264
269
|
| `getTenantData` | `TenantData` | `outdialEnabled`, `forceDefaultDn`, `privacyShieldVisible`, `timeoutDesktopInactivityEnabled` | Tenant-level feature flags |
|
|
265
270
|
| `getOrgInfo` | `OrgInfo` | `tenantId`, `timezone` | Organization metadata |
|
|
266
271
|
| `getAllAuxCodes` | `AuxCode[]` | `id`, `name`, `workTypeCode`, `active`, `isSystemCode`, `defaultCode` | Auxiliary codes for idle/wrap-up states |
|
|
267
|
-
| `getOrganizationSetting` | `OrgSettings` | `webRtcEnabled`, `maskSensitiveData`, `campaignManagerEnabled` | Organization-level feature flags |
|
|
272
|
+
| `getOrganizationSetting` | `OrgSettings` | `webRtcEnabled`, `maskSensitiveData`, `campaignManagerEnabled`, `aiAssistantQuantity` | Organization-level feature flags and AI Assistant license quantity |
|
|
268
273
|
| `getDialPlanData` | `DialPlanEntity[]` | `id`, `name`, `regularExpression`, `prefix`, `strippedChars` | Dial plan rules for outbound calling |
|
|
269
274
|
| `getURLMapping` | `URLMapping[]` | `name`, `url` | External service URL mappings |
|
|
270
275
|
| `getSiteInfo` | `SiteInfo` | Site-specific configuration | Site details |
|
|
@@ -449,11 +454,14 @@ classDiagram
|
|
|
449
454
|
- **UC-2 Paginated teams/aux codes:** follow backend page metadata to completion; auxiliary-code requests include `desktopProfileFilter=true`. Evidence: `src/services/config/index.ts`, `test/unit/spec/services/config/index.ts`.
|
|
450
455
|
- **UC-3 Dial plan and URL mapping:** request dial-plan data only when the desktop profile enables it while always including URL mapping in profile aggregation. Evidence: `src/services/config/index.ts`, `test/unit/spec/services/config/index.ts`.
|
|
451
456
|
- **UC-4 Outdial ANI retrieval:** return the organization-scoped ANI list through authenticated WebexRequest and propagate failures without a partial substitute. Evidence: `src/services/config/index.ts`, `test/unit/spec/services/config/index.ts`.
|
|
457
|
+
- **UC-5 Wellness configuration:** produce one fail-closed effective profile flag and retrieve the exact system wellness idle code across pages. Evidence: `src/services/config/Util.ts`, `src/services/config/index.ts`, `test/unit/spec/services/config/Util.ts`, `test/unit/spec/services/config/index.ts`.
|
|
452
458
|
|
|
453
459
|
## Business Rules & Invariants
|
|
454
460
|
- `getAgentConfig` rejects when any required dependent request rejects; it never returns a partial Profile.
|
|
455
461
|
- `Profile.aiFeature` is derived from the AI-feature response, and sensitive-data masking uses the real `maskSensitiveData` field.
|
|
456
462
|
- Team data has no `channelMap` contract; multimedia profile responses use `MultimediaProfileResponse`.
|
|
463
|
+
- `isWellnessBreakEnabled` is always boolean and defaults false when any gate is absent, disabled, or unlicensed.
|
|
464
|
+
- The system wellness code lookup is separate from profile auxiliary-code aggregation and uses `desktopProfileFilter=false`.
|
|
457
465
|
|
|
458
466
|
## Concurrency & Reactive Flow
|
|
459
467
|
- The initial user lookup supplies identifiers for a ten-promise `Promise.all`; pagination loops await pages in order and stop from returned metadata.
|
|
@@ -465,6 +473,7 @@ All Config operations are authenticated REST calls through WebexRequest. Importa
|
|
|
465
473
|
|---|---|
|
|
466
474
|
| AI feature flags | `organization/{orgId}/v2/ai-feature?page=0&pageSize=100` |
|
|
467
475
|
| Auxiliary codes | pagination/filter/attributes plus `desktopProfileFilter=true` |
|
|
476
|
+
| Wellness system idle code | `organization/{orgId}/v2/auxiliary-code?page={page}&pageSize={pageSize}&workType=IDLE_CODE&customFilter=isSystemCode==true&desktopProfileFilter=false` |
|
|
468
477
|
| Multimedia profile | organization-scoped multimedia-profile resource |
|
|
469
478
|
| Dial plan | organization-scoped dial-plan resource when desktop profile enables it |
|
|
470
479
|
|
|
@@ -653,6 +662,8 @@ public async getUserUsingCI(orgId: string, agentId: string): Promise<AgentRespon
|
|
|
653
662
|
| `CONFIG-R-003` | `test/unit/spec/services/config/index.ts` | None. |
|
|
654
663
|
| `CONFIG-R-004` | `test/unit/spec/services/config/index.ts` | None. |
|
|
655
664
|
| `CONFIG-R-005` | `test/unit/spec/services/config/index.ts` | Keep an explicit rejection assertion for each required dependency category. |
|
|
665
|
+
| `CONFIG-R-006` | `test/unit/spec/services/config/Util.ts` | None. |
|
|
666
|
+
| `CONFIG-R-007` | `test/unit/spec/services/config/index.ts` | None. |
|
|
656
667
|
|
|
657
668
|
## Traceability
|
|
658
669
|
- Repo architecture: `../../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../../ai-docs/SPEC_INDEX.md`
|
|
@@ -34,6 +34,12 @@ export const AGENT_STATE_AVAILABLE_ID = '0';
|
|
|
34
34
|
*/
|
|
35
35
|
export const AGENT_STATE_AVAILABLE = 'Available';
|
|
36
36
|
|
|
37
|
+
/** System idle-code name reserved for Agent Wellness Break. */
|
|
38
|
+
export const WELLBEING_BREAK_IDLE_CODE = 'WellbeingBreak';
|
|
39
|
+
|
|
40
|
+
/** Backend sentinel indicating wellness-break reminders are enabled. @internal */
|
|
41
|
+
export const WELLNESS_BREAK_REMINDERS_ENABLED = 'ENABLED';
|
|
42
|
+
|
|
37
43
|
/**
|
|
38
44
|
* Description for the 'Available' agent state.
|
|
39
45
|
* @type {string}
|
|
@@ -75,6 +81,7 @@ export const METHODS = {
|
|
|
75
81
|
GET_URL_MAPPING: 'getURLMapping',
|
|
76
82
|
GET_DIAL_PLAN_DATA: 'getDialPlanData',
|
|
77
83
|
GET_AI_FEATURE_FLAGS: 'getAIFeatureFlags',
|
|
84
|
+
GET_WELLBEING_BREAK_IDLE_CODE: 'getWellbeingBreakIdleCode',
|
|
78
85
|
GET_QUEUES: 'getQueues',
|
|
79
86
|
|
|
80
87
|
// Util methods
|
|
@@ -175,6 +182,11 @@ export const endPointMap = {
|
|
|
175
182
|
filter && filter.length > 0 ? `&filter=id=in=(${filter})` : ''
|
|
176
183
|
}&attributes=${attributes}&desktopProfileFilter=true`,
|
|
177
184
|
|
|
185
|
+
/** Lists system idle codes without applying the agent Desktop Profile filter. */
|
|
186
|
+
systemIdleCodes: (orgId: string, page: number, pageSize: number) =>
|
|
187
|
+
`organization/${orgId}/v2/auxiliary-code?page=${page}&pageSize=${pageSize}` +
|
|
188
|
+
'&workType=IDLE_CODE&customFilter=isSystemCode==true&desktopProfileFilter=false',
|
|
189
|
+
|
|
178
190
|
/**
|
|
179
191
|
* Gets the endpoint for organization info.
|
|
180
192
|
* @param orgId - Organization ID.
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
OutdialAniEntriesResponse,
|
|
24
24
|
OutdialAniParams,
|
|
25
25
|
AIFeatureFlagsResponse,
|
|
26
|
+
Entity,
|
|
26
27
|
} from './types';
|
|
27
28
|
import WebexRequest from '../core/WebexRequest';
|
|
28
29
|
import {WCC_API_GATEWAY} from '../constants';
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
DEFAULT_PAGE_SIZE,
|
|
35
36
|
endPointMap,
|
|
36
37
|
METHODS,
|
|
38
|
+
WELLBEING_BREAK_IDLE_CODE,
|
|
37
39
|
} from './constants';
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -470,6 +472,69 @@ export default class AgentConfigService {
|
|
|
470
472
|
}
|
|
471
473
|
}
|
|
472
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Retrieves the exact system-managed WellbeingBreak idle code.
|
|
477
|
+
* This query intentionally bypasses Desktop Profile filtering so the system code does not
|
|
478
|
+
* need to appear in the normal agent-selectable idle-code list.
|
|
479
|
+
* @param orgId - Organization identifier
|
|
480
|
+
* @returns The normalized WellbeingBreak entity
|
|
481
|
+
* @throws When the request fails or the exact system code is unavailable
|
|
482
|
+
* @internal
|
|
483
|
+
*/
|
|
484
|
+
public async getWellbeingBreakIdleCode(orgId: string): Promise<Entity> {
|
|
485
|
+
LoggerProxy.info('Fetching WellbeingBreak system idle code', {
|
|
486
|
+
module: CONFIG_FILE_NAME,
|
|
487
|
+
method: METHODS.GET_WELLBEING_BREAK_IDLE_CODE,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
let page = DEFAULT_PAGE;
|
|
492
|
+
let totalPages = 1;
|
|
493
|
+
|
|
494
|
+
do {
|
|
495
|
+
// Pagination is intentionally sequential so the lookup can stop as soon as the code is found.
|
|
496
|
+
// eslint-disable-next-line no-await-in-loop
|
|
497
|
+
const response = await this.webexReq.request({
|
|
498
|
+
service: WCC_API_GATEWAY,
|
|
499
|
+
resource: endPointMap.systemIdleCodes(orgId, page, DEFAULT_PAGE_SIZE),
|
|
500
|
+
method: HTTP_METHODS.GET,
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
if (response.statusCode !== 200) {
|
|
504
|
+
throw new Error(`API call failed with ${response.statusCode}`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const result = response.body as ListAuxCodesResponse;
|
|
508
|
+
const wellbeingCode = result.data?.find(
|
|
509
|
+
(code) =>
|
|
510
|
+
code.active === true &&
|
|
511
|
+
code.isSystemCode === true &&
|
|
512
|
+
code.name === WELLBEING_BREAK_IDLE_CODE
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
if (wellbeingCode) {
|
|
516
|
+
return {
|
|
517
|
+
id: wellbeingCode.id,
|
|
518
|
+
name: wellbeingCode.name,
|
|
519
|
+
isSystem: true,
|
|
520
|
+
isDefault: wellbeingCode.defaultCode,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
totalPages = result.meta?.totalPages ?? 1;
|
|
525
|
+
page += 1;
|
|
526
|
+
} while (page < totalPages);
|
|
527
|
+
|
|
528
|
+
throw new Error('WELLBEING_BREAK_IDLE_CODE_NOT_FOUND');
|
|
529
|
+
} catch (error) {
|
|
530
|
+
LoggerProxy.error(`getWellbeingBreakIdleCode API call failed with ${error}`, {
|
|
531
|
+
module: CONFIG_FILE_NAME,
|
|
532
|
+
method: METHODS.GET_WELLBEING_BREAK_IDLE_CODE,
|
|
533
|
+
});
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
473
538
|
/**
|
|
474
539
|
* Fetches the site data for the given orgId and siteId.
|
|
475
540
|
* @ignore
|
|
@@ -150,8 +150,7 @@ export const CC_TASK_EVENTS = {
|
|
|
150
150
|
/**
|
|
151
151
|
* Events emitted on Contact Center agent operations
|
|
152
152
|
* @enum {string}
|
|
153
|
-
* @
|
|
154
|
-
* @ignore
|
|
153
|
+
* @public
|
|
155
154
|
*/
|
|
156
155
|
export const CC_AGENT_EVENTS = {
|
|
157
156
|
/** Welcome event when agent connects to websocket/backend */
|
|
@@ -190,6 +189,8 @@ export const CC_AGENT_EVENTS = {
|
|
|
190
189
|
AGENT_BUDDY_AGENTS_RETRIEVE_FAILED: 'BuddyAgentsRetrieveFailed',
|
|
191
190
|
/** Event emitted when contact is reserved for agent */
|
|
192
191
|
AGENT_CONTACT_RESERVED: 'AgentContactReserved',
|
|
192
|
+
/** Agent-level Agent Wellness Break notification */
|
|
193
|
+
WELLNESS_BREAK: 'WellnessBreak',
|
|
193
194
|
} as const;
|
|
194
195
|
|
|
195
196
|
/**
|
|
@@ -729,6 +730,8 @@ export type OrgSettings = {
|
|
|
729
730
|
maskSensitiveData: boolean;
|
|
730
731
|
/** Whether campaign manager features are enabled */
|
|
731
732
|
campaignManagerEnabled: boolean;
|
|
733
|
+
/** Number of AI Assistant licenses assigned to the organization */
|
|
734
|
+
aiAssistantQuantity?: number | null;
|
|
732
735
|
};
|
|
733
736
|
|
|
734
737
|
/**
|
|
@@ -1237,6 +1240,8 @@ export type Profile = {
|
|
|
1237
1240
|
maskSensitiveData?: boolean;
|
|
1238
1241
|
/** Whether agent is currently logged in */
|
|
1239
1242
|
isAgentLoggedIn?: boolean;
|
|
1243
|
+
/** Active station session after a successful registered relogin. */
|
|
1244
|
+
agentSessionId?: string;
|
|
1240
1245
|
/** Last auxiliary code ID used for state change */
|
|
1241
1246
|
lastStateAuxCodeId?: string;
|
|
1242
1247
|
/** Timestamp of last state change */
|
|
@@ -1245,6 +1250,12 @@ export type Profile = {
|
|
|
1245
1250
|
lastIdleCodeChangeTimestamp?: number;
|
|
1246
1251
|
/** AI feature flags resolved from organization config */
|
|
1247
1252
|
aiFeature?: AIFeatureFlags;
|
|
1253
|
+
/**
|
|
1254
|
+
* Whether Agent Wellness Break is effectively enabled for this profile.
|
|
1255
|
+
* This combines the organization wellbeing configuration with AI Assistant licensing;
|
|
1256
|
+
* Server-side rollout remains enforced by the backend configuration and notification delivery.
|
|
1257
|
+
*/
|
|
1258
|
+
isWellnessBreakEnabled?: boolean;
|
|
1248
1259
|
};
|
|
1249
1260
|
|
|
1250
1261
|
/**
|
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
import {PARTICIPANT_TYPES, STATE_CONSULT} from './constants';
|
|
17
17
|
import {DialPlan} from '../config/types';
|
|
18
18
|
|
|
19
|
+
/** Returns whether a value is a non-null object with string keys. */
|
|
20
|
+
export const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
21
|
+
typeof value === 'object' && value !== null;
|
|
22
|
+
|
|
19
23
|
/**
|
|
20
24
|
* Extracts common error details from a Webex request payload.
|
|
21
25
|
*
|
|
@@ -31,6 +31,7 @@ export default class TaskFactory {
|
|
|
31
31
|
isRecordingEnabled: recordingEnabled,
|
|
32
32
|
enableWxBetterTogether: configFlags.enableWxBetterTogether ?? false,
|
|
33
33
|
answerCallOnWebexService,
|
|
34
|
+
getUsersubPublished: configFlags.getWxAppUsersubPublished,
|
|
34
35
|
consultTransferConfig: consultTransfer,
|
|
35
36
|
};
|
|
36
37
|
switch (mediaType) {
|
|
@@ -778,6 +778,7 @@ export default class TaskManager extends EventEmitter {
|
|
|
778
778
|
const interactionId = message.data.interactionId;
|
|
779
779
|
const reservationInteractionId = message.data.reservationInteractionId;
|
|
780
780
|
let task = this.taskCollection[interactionId];
|
|
781
|
+
let ownerChangeResolvedByRelatedInteraction = false;
|
|
781
782
|
|
|
782
783
|
// When a campaign preview contact is accepted, the assigned event may arrive
|
|
783
784
|
// with a new interactionId while the task is stored under the original
|
|
@@ -792,7 +793,66 @@ export default class TaskManager extends EventEmitter {
|
|
|
792
793
|
}
|
|
793
794
|
|
|
794
795
|
if (!task && eventType === CC_EVENTS.CONTACT_OWNER_CHANGED) {
|
|
795
|
-
|
|
796
|
+
const incomingShowsCurrentAgentPromotion = Boolean(
|
|
797
|
+
this.agentId &&
|
|
798
|
+
message.data.interaction?.owner === this.agentId &&
|
|
799
|
+
TaskManager.isParticipantActiveOnMainInteraction(message.data, this.agentId)
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
task = this.findUniqueTaskByRelatedInteraction(
|
|
803
|
+
message.data,
|
|
804
|
+
(candidate) => {
|
|
805
|
+
const currentAgentId = this.agentId || candidate.data?.agentId;
|
|
806
|
+
|
|
807
|
+
return Boolean(
|
|
808
|
+
currentAgentId &&
|
|
809
|
+
(TaskManager.isParticipantActiveOnMainInteraction(candidate.data, currentAgentId) ||
|
|
810
|
+
incomingShowsCurrentAgentPromotion)
|
|
811
|
+
);
|
|
812
|
+
},
|
|
813
|
+
{includeCollectionKeys: true, includeMainCallMediaKeys: true}
|
|
814
|
+
);
|
|
815
|
+
ownerChangeResolvedByRelatedInteraction = Boolean(task);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
let ownerChangeMainInteractionId: string | undefined;
|
|
819
|
+
if (task && eventType === CC_EVENTS.CONTACT_OWNER_CHANGED) {
|
|
820
|
+
const taskMainInteractionId = task.data?.interaction?.mainInteractionId;
|
|
821
|
+
const payloadMainInteractionId = message.data.interaction?.mainInteractionId;
|
|
822
|
+
const hasConflictingMainInteractionIds = Boolean(
|
|
823
|
+
taskMainInteractionId &&
|
|
824
|
+
payloadMainInteractionId &&
|
|
825
|
+
taskMainInteractionId !== payloadMainInteractionId
|
|
826
|
+
);
|
|
827
|
+
// A promoted child task can retain stale embedded IDs. Prefer the
|
|
828
|
+
// authoritative incoming main-call identity before local snapshot IDs.
|
|
829
|
+
const stableMainInteractionId =
|
|
830
|
+
payloadMainInteractionId ||
|
|
831
|
+
TaskManager.getMainCallMediaId(message.data.interaction) ||
|
|
832
|
+
TaskManager.getMainCallMediaId(task.data?.interaction) ||
|
|
833
|
+
taskMainInteractionId ||
|
|
834
|
+
message.data.interaction?.interactionId ||
|
|
835
|
+
task.data?.interaction?.interactionId ||
|
|
836
|
+
task.data?.interactionId;
|
|
837
|
+
const taskAtStableKey = stableMainInteractionId
|
|
838
|
+
? this.taskCollection[stableMainInteractionId]
|
|
839
|
+
: undefined;
|
|
840
|
+
|
|
841
|
+
if (
|
|
842
|
+
!stableMainInteractionId ||
|
|
843
|
+
hasConflictingMainInteractionIds ||
|
|
844
|
+
(taskAtStableKey && taskAtStableKey !== task)
|
|
845
|
+
) {
|
|
846
|
+
LoggerProxy.warn('Unable to safely correlate owner change to the main interaction task', {
|
|
847
|
+
module: TASK_MANAGER_FILE,
|
|
848
|
+
method: 'prepareEventContext',
|
|
849
|
+
interactionId:
|
|
850
|
+
payloadMainInteractionId || taskMainInteractionId || message.data.interactionId,
|
|
851
|
+
});
|
|
852
|
+
task = undefined;
|
|
853
|
+
} else if (ownerChangeResolvedByRelatedInteraction) {
|
|
854
|
+
ownerChangeMainInteractionId = stableMainInteractionId;
|
|
855
|
+
}
|
|
796
856
|
}
|
|
797
857
|
|
|
798
858
|
if (!task && MAIN_INTERACTION_CORRELATED_EVENTS.has(eventType)) {
|
|
@@ -821,8 +881,11 @@ export default class TaskManager extends EventEmitter {
|
|
|
821
881
|
}
|
|
822
882
|
: message.data;
|
|
823
883
|
|
|
824
|
-
if (task &&
|
|
825
|
-
adjustedPayload =
|
|
884
|
+
if (task && ownerChangeMainInteractionId) {
|
|
885
|
+
adjustedPayload = {
|
|
886
|
+
...adjustedPayload,
|
|
887
|
+
interactionId: ownerChangeMainInteractionId,
|
|
888
|
+
};
|
|
826
889
|
}
|
|
827
890
|
|
|
828
891
|
if (task && eventType === CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE) {
|
|
@@ -860,53 +923,6 @@ export default class TaskManager extends EventEmitter {
|
|
|
860
923
|
};
|
|
861
924
|
}
|
|
862
925
|
|
|
863
|
-
/**
|
|
864
|
-
* ContactOwnerChanged can identify the promoted agent's child interaction while
|
|
865
|
-
* the surviving task is stored under the main interaction. Accept one related
|
|
866
|
-
* task when its existing snapshot or the authoritative incoming promotion proves
|
|
867
|
-
* current-agent main-call membership, while excluding non-promoted and ambiguous matches.
|
|
868
|
-
*/
|
|
869
|
-
private findTaskForContactOwnerChange(payload: WebSocketPayload): ITask | undefined {
|
|
870
|
-
const relatedCandidates = this.findRelatedTasks(payload, {
|
|
871
|
-
includeCollectionKeys: true,
|
|
872
|
-
includeMainCallMediaKeys: true,
|
|
873
|
-
});
|
|
874
|
-
const incomingShowsCurrentAgentPromotion =
|
|
875
|
-
this.isCurrentAgentPromotedOnIncomingMainCall(payload);
|
|
876
|
-
const eligibleCandidates = relatedCandidates.filter(
|
|
877
|
-
(candidate) =>
|
|
878
|
-
this.isCurrentAgentActiveOnMainInteraction(candidate.data) ||
|
|
879
|
-
incomingShowsCurrentAgentPromotion
|
|
880
|
-
);
|
|
881
|
-
|
|
882
|
-
if (eligibleCandidates.length === 1) {
|
|
883
|
-
return eligibleCandidates[0];
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (eligibleCandidates.length > 1) {
|
|
887
|
-
LoggerProxy.warn('Unable to correlate task event to a unique main interaction task', {
|
|
888
|
-
module: TASK_MANAGER_FILE,
|
|
889
|
-
method: 'findTaskForContactOwnerChange',
|
|
890
|
-
interactionId:
|
|
891
|
-
payload.interaction?.mainInteractionId ||
|
|
892
|
-
payload.interaction?.parentInteractionId ||
|
|
893
|
-
payload.interaction?.callProcessingDetails?.parentInteractionId ||
|
|
894
|
-
payload.interactionId,
|
|
895
|
-
});
|
|
896
|
-
|
|
897
|
-
return undefined;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
return undefined;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
private isCurrentAgentActiveOnMainInteraction(taskData: TaskData | undefined): boolean {
|
|
904
|
-
const currentAgentId = this.agentId || taskData?.agentId;
|
|
905
|
-
if (!currentAgentId) return false;
|
|
906
|
-
|
|
907
|
-
return TaskManager.isParticipantActiveOnMainInteraction(taskData, currentAgentId);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
926
|
private static isParticipantActiveOnMainInteraction(
|
|
911
927
|
taskData: TaskData | WebSocketPayload | undefined,
|
|
912
928
|
participantId: string
|
|
@@ -921,14 +937,6 @@ export default class TaskManager extends EventEmitter {
|
|
|
921
937
|
);
|
|
922
938
|
}
|
|
923
939
|
|
|
924
|
-
private isCurrentAgentPromotedOnIncomingMainCall(payload: WebSocketPayload): boolean {
|
|
925
|
-
return Boolean(
|
|
926
|
-
this.agentId &&
|
|
927
|
-
payload.interaction?.owner === this.agentId &&
|
|
928
|
-
TaskManager.isParticipantActiveOnMainInteraction(payload, this.agentId)
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
940
|
private static getMainCallMediaId(
|
|
933
941
|
interaction: TaskData['interaction'] | undefined
|
|
934
942
|
): string | undefined {
|
|
@@ -960,31 +968,6 @@ export default class TaskManager extends EventEmitter {
|
|
|
960
968
|
);
|
|
961
969
|
}
|
|
962
970
|
|
|
963
|
-
/** Keep a related owner-change event from re-keying the surviving main task to a child ID. */
|
|
964
|
-
private static preserveMainTaskIdentity(
|
|
965
|
-
task: ITask,
|
|
966
|
-
payload: WebSocketPayload
|
|
967
|
-
): WebSocketPayload {
|
|
968
|
-
const currentMainInteractionId = task.data?.interaction?.mainInteractionId;
|
|
969
|
-
const payloadMainInteractionId = payload.interaction?.mainInteractionId;
|
|
970
|
-
const currentMainMediaId = TaskManager.getMainCallMediaId(task.data?.interaction);
|
|
971
|
-
const payloadMainMediaId = TaskManager.getMainCallMediaId(payload.interaction);
|
|
972
|
-
const stableInteractionId =
|
|
973
|
-
currentMainInteractionId ||
|
|
974
|
-
payloadMainInteractionId ||
|
|
975
|
-
currentMainMediaId ||
|
|
976
|
-
payloadMainMediaId ||
|
|
977
|
-
payload.interaction?.interactionId ||
|
|
978
|
-
task.data?.interaction?.interactionId ||
|
|
979
|
-
task.data?.interactionId;
|
|
980
|
-
|
|
981
|
-
if (!stableInteractionId || stableInteractionId === payload.interactionId) {
|
|
982
|
-
return payload;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
return {...payload, interactionId: stableInteractionId};
|
|
986
|
-
}
|
|
987
|
-
|
|
988
971
|
/**
|
|
989
972
|
* ParticipantLeftConference may arrive after an owner update while still carrying
|
|
990
973
|
* the departed owner. Keep the confirmed owner only when the new roster proves that
|
|
@@ -1024,9 +1007,10 @@ export default class TaskManager extends EventEmitter {
|
|
|
1024
1007
|
*/
|
|
1025
1008
|
private findUniqueTaskByRelatedInteraction(
|
|
1026
1009
|
payload: WebSocketPayload,
|
|
1027
|
-
candidateFilter: (task: ITask) => boolean = () => true
|
|
1010
|
+
candidateFilter: (task: ITask) => boolean = () => true,
|
|
1011
|
+
options: {includeCollectionKeys?: boolean; includeMainCallMediaKeys?: boolean} = {}
|
|
1028
1012
|
): ITask | undefined {
|
|
1029
|
-
const candidates = this.findRelatedTasks(payload).filter(candidateFilter);
|
|
1013
|
+
const candidates = this.findRelatedTasks(payload, options).filter(candidateFilter);
|
|
1030
1014
|
|
|
1031
1015
|
if (candidates.length === 1) {
|
|
1032
1016
|
return candidates[0];
|
|
@@ -1060,7 +1044,9 @@ export default class TaskManager extends EventEmitter {
|
|
|
1060
1044
|
payload.interaction?.parentInteractionId,
|
|
1061
1045
|
payload.interaction?.callProcessingDetails?.parentInteractionId,
|
|
1062
1046
|
...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(payload.interaction)] : []),
|
|
1063
|
-
].filter((
|
|
1047
|
+
].filter((relatedInteractionId): relatedInteractionId is string =>
|
|
1048
|
+
Boolean(relatedInteractionId)
|
|
1049
|
+
)
|
|
1064
1050
|
);
|
|
1065
1051
|
if (payloadInteractionIds.size === 0) return [];
|
|
1066
1052
|
|
|
@@ -1082,7 +1068,8 @@ export default class TaskManager extends EventEmitter {
|
|
|
1082
1068
|
];
|
|
1083
1069
|
|
|
1084
1070
|
return candidateInteractionIds.some(
|
|
1085
|
-
(
|
|
1071
|
+
(relatedInteractionId) =>
|
|
1072
|
+
Boolean(relatedInteractionId) && payloadInteractionIds.has(relatedInteractionId)
|
|
1086
1073
|
);
|
|
1087
1074
|
})
|
|
1088
1075
|
.map(([, candidate]) => candidate)
|
|
@@ -1457,6 +1444,7 @@ export default class TaskManager extends EventEmitter {
|
|
|
1457
1444
|
}
|
|
1458
1445
|
|
|
1459
1446
|
private removeTaskFromCollection(task: ITask) {
|
|
1447
|
+
task.disposeWxAppOfferObservability?.();
|
|
1460
1448
|
if (typeof task.cancelAutoWrapupTimer === 'function') {
|
|
1461
1449
|
task.cancelAutoWrapupTimer();
|
|
1462
1450
|
}
|
|
@@ -1655,6 +1643,16 @@ export default class TaskManager extends EventEmitter {
|
|
|
1655
1643
|
});
|
|
1656
1644
|
}
|
|
1657
1645
|
|
|
1646
|
+
/**
|
|
1647
|
+
* Re-run OFFERED wxApp offer diagnostics for collected voice tasks.
|
|
1648
|
+
* Used after usersub publish so pre-publish offers can schedule mismatch grace.
|
|
1649
|
+
*/
|
|
1650
|
+
public refreshWxAppOfferObservabilityForAllTasks(): void {
|
|
1651
|
+
Object.values(this.taskCollection).forEach((task) => {
|
|
1652
|
+
task.refreshWxAppOfferObservability?.();
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1658
1656
|
public static getTaskManager(
|
|
1659
1657
|
apiAIAssistant: ApiAIAssistant,
|
|
1660
1658
|
contact: ReturnType<typeof routingContact>,
|
|
@@ -618,6 +618,8 @@ This keeps transcript and suggestion delivery aligned on the same per-task event
|
|
|
618
618
|
|
|
619
619
|
**wxApp consumer contract (WXCC-6026):** Hosts enable `enableWxBetterTogether` at init (Phase 1 init-only; re-init to change), bind UI to `task.uiControls` (including optional `main.keypad`), and call **`task.accept()`**, **`task.decline()`**, **`task.toggleMute({ muted? })`**, **`task.transmitDtmf({ dtmf })`**. SDK routes wxApp telephony internally on `Voice`. Shared-line `lineOwnerId` defaults from the wxApp agent participant when omitted.
|
|
620
620
|
|
|
621
|
+
**wxApp OFFERED diagnostics:** `Voice.updateUiControls` delegates offer-decision logging and deferred participant-mismatch WARN/metric to `voice/wxAppOfferObservability.ts` (500 ms grace; observability only). After usersub publish, `TaskManager.refreshWxAppOfferObservabilityForAllTasks()` re-invokes the observer without emitting UI controls. `removeTaskFromCollection` calls `disposeWxAppOfferObservability()` so a pending grace timer cannot emit after the task is dropped (including `CONTACT_MERGED` child removal). Dispose is terminal: the observer sets a disposed guard (timer cancel plus no-op `handleUiControlsUpdate`) so a later `updateUiControls` / refresh cannot schedule a new grace timer.
|
|
622
|
+
|
|
621
623
|
**wxApp offer UI (`uiControlsComputer`):** `wxAppAcceptInFlight` disables accept/decline during the accept REST call. `wxAppAnswerPending` additionally disables accept and decline for **inbound** offers until ASSIGN; wxApp **outdial** keeps decline enabled during the post-accept "Calling…" phase so `cancelTask` remains available.
|
|
622
624
|
|
|
623
625
|
**wxApp decline observability:** Inbound wxApp offers decline via telephony `rejectCall` (`runWxAppReject` → `WXAPP_TASK_DECLINE_*`). wxApp outdial cancellations use CC routing `cancelTask` (`runWxAppOutdialDecline` → additive `WXAPP_TASK_DECLINE_*` plus existing `TASK_DECLINE_*`).
|
|
@@ -924,15 +926,17 @@ guards. The state-machine transition table lives in
|
|
|
924
926
|
Primary-Agent promotion remains backend-authoritative and follows the two-event
|
|
925
927
|
desktop contract. `ContactOwnerChanged` updates the promoted Agent; TaskManager
|
|
926
928
|
prefers an exact task, then one unique related task resolved through nested
|
|
927
|
-
main/parent identifiers or the `mainCall` media-map
|
|
928
|
-
candidate is eligible when its current
|
|
929
|
+
main/parent identifiers, the task collection key, or the `mainCall` media-map
|
|
930
|
+
identity. A related candidate is eligible when its current
|
|
929
931
|
snapshot contains the current Agent's participant entry with `hasLeft !== true`
|
|
930
932
|
and membership on the `mType: mainCall` leg. The incoming snapshot can provide
|
|
931
933
|
that evidence only when it also names the current Agent as `interaction.owner`;
|
|
932
934
|
this permits an authoritative promotion payload to repair a stale child-keyed
|
|
933
935
|
snapshot while still excluding consult-only tasks. The update keeps the
|
|
934
936
|
surviving main interaction identity even when the notification names a
|
|
935
|
-
promoted-Agent child interaction.
|
|
937
|
+
promoted-Agent child interaction. Conflicting main identifiers, ambiguous
|
|
938
|
+
matches, and a canonical key occupied by a distinct task are ignored rather
|
|
939
|
+
than replacing an existing task.
|
|
936
940
|
|
|
937
941
|
If no related task exists, TaskManager recovers only the promoted current Agent
|
|
938
942
|
from a non-terminal telephony `ContactOwnerChanged` payload that provides the same
|
|
@@ -1759,6 +1759,20 @@ export interface ITask extends IEventEmitter {
|
|
|
1759
1759
|
*/
|
|
1760
1760
|
syncWxAppMuteFromCallDetails?(): Promise<boolean | undefined>;
|
|
1761
1761
|
|
|
1762
|
+
/**
|
|
1763
|
+
* Re-run OFFERED wxApp offer diagnostics without emitting UI controls.
|
|
1764
|
+
* Voice tasks implement this; other channel types omit it.
|
|
1765
|
+
* @internal
|
|
1766
|
+
*/
|
|
1767
|
+
refreshWxAppOfferObservability?(): void;
|
|
1768
|
+
|
|
1769
|
+
/**
|
|
1770
|
+
* Cancel pending wxApp offer observability timers (participant-mismatch grace).
|
|
1771
|
+
* Voice tasks implement this; other channel types omit it.
|
|
1772
|
+
* @internal
|
|
1773
|
+
*/
|
|
1774
|
+
disposeWxAppOfferObservability?(): void;
|
|
1775
|
+
|
|
1762
1776
|
/**
|
|
1763
1777
|
* State machine instance for managing task state transitions and derived properties.
|
|
1764
1778
|
* The state machine handles:
|
|
@@ -2070,6 +2084,8 @@ export type VoiceUIControlOptions = {
|
|
|
2070
2084
|
enableWxBetterTogether?: boolean;
|
|
2071
2085
|
consultTransferConfig?: ConsultTransferDestinationConfig;
|
|
2072
2086
|
answerCallOnWebexService?: import('../AnswerCallOnWebexService').default;
|
|
2087
|
+
/** Read-only observability: whether usersub answer-calls-on-wxcc is active this session. */
|
|
2088
|
+
getUsersubPublished?: () => boolean;
|
|
2073
2089
|
};
|
|
2074
2090
|
|
|
2075
2091
|
/**
|