@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.
|
|
@@ -246,6 +246,7 @@ All event names are defined in `METRIC_EVENT_NAMES` (`constants.ts`). Events fol
|
|
|
246
246
|
| `WXAPP_SESSION_SKIPPED` | `'WxApp Session Skipped'` | Webex Together session skipped (`wxcc_sdk.user.webex_together_session_init.ignore`) |
|
|
247
247
|
| `WXAPP_USERSUB_PUBLISH_SUCCESS` / `FAILED` | `'WxApp Usersub Publish ...'` | Cross-client usersub publish (`wxcc_sdk.user.webex_together_usersub_publish.complete\|fail`). Preflight failures (missing `userId` or device URL before HTTP publish) emit `WXAPP_USERSUB_PUBLISH_FAILED` with `skipReason` (`user_id_unavailable` / `device_url_unavailable`) and no `timeEvent` timer. Stale-generation discards (teardown or concurrent disable while HTTP publish is in flight) call `cancelTimedEvent` without emitting success/fail only when the completing publish still owns the active timer (`usersubPublishMetricsId` guard — a newer overlapping publish must not have its timer cleared). |
|
|
248
248
|
| `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` / `FAILED` | `'WxApp Mercury Subscribe ...'` | Telephony Mercury mute-sync subscribe (`wxcc_sdk.user.webex_together_mercury_subscribe.complete\|fail`) |
|
|
249
|
+
| `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` | `'WxApp Offer Participant Fields Missing'` | Read-only observability when usersub is active but inbound OFFERED participant **still** lacks valid wxApp fields after a **500 ms grace period** (orchestrated in `voice/wxAppOfferObservability.ts`; avoids false alarms from transient WS ordering). After usersub publish, `TaskManager.refreshWxAppOfferObservabilityForAllTasks()` re-runs the observer so a pre-publish OFFERED task can still schedule grace. Task removal calls `disposeWxAppOfferObservability()` so a pending timer cannot emit after `CONTACT_MERGED` / collection drop. Emitted with matching `[CC wxApp] participant fields mismatch` WARN (`wxcc_sdk.user.webex_together_offer_participant_fields.fail`) |
|
|
249
250
|
| `TASK_CONFERENCE_START_SUCCESS` / `FAILED` | `'Task Conference Start ...'` | Conference start result |
|
|
250
251
|
| `TASK_CONFERENCE_END_SUCCESS` / `FAILED` | `'Task Conference End ...'` | Conference end result |
|
|
251
252
|
| `TASK_CONFERENCE_TRANSFER_SUCCESS` / `FAILED` | `'Task Conference Transfer ...'` | Conference transfer result |
|
|
@@ -260,6 +261,9 @@ All event names are defined in `METRIC_EVENT_NAMES` (`constants.ts`). Events fol
|
|
|
260
261
|
| `ADDRESSBOOK_FETCH_SUCCESS` / `FAILED` | `'AddressBook Fetch ...'` | Address book fetch result |
|
|
261
262
|
| `QUEUE_FETCH_SUCCESS` / `FAILED` | `'Queue Fetch ...'` | Queue fetch result |
|
|
262
263
|
| `OUTDIAL_ANI_EP_FETCH_SUCCESS` / `FAILED` | `'Outdial ANI Entries Fetch ...'` | Outdial ANI entries fetch result |
|
|
264
|
+
| `AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED` / `FAILED` | `'AI Assistant Wellness Action ...'` | Direct wellness custom-event result |
|
|
265
|
+
| `AI_ASSISTANT_WELLNESS_EVENT_INVALID` | `'AI Assistant Wellness Event Invalid'` | Malformed or mismatched wellness notification ignored |
|
|
266
|
+
| `WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS` / `FAILED` | `'Wellbeing Break Idle Code Fetch ...'` | System wellness idle-code lookup result |
|
|
263
267
|
|
|
264
268
|
All event names are defined in `constants.ts` as `METRIC_EVENT_NAMES`. Events follow a `{Domain} {Action} {Success|Failed}` naming convention:
|
|
265
269
|
|
|
@@ -298,6 +302,7 @@ All event names are defined in `constants.ts` as `METRIC_EVENT_NAMES`. Events fo
|
|
|
298
302
|
| WxApp Session Init | `WXAPP_SESSION_INIT_SUCCESS` | `WXAPP_SESSION_INIT_FAILED` |
|
|
299
303
|
| WxApp Usersub Publish | `WXAPP_USERSUB_PUBLISH_SUCCESS` | `WXAPP_USERSUB_PUBLISH_FAILED` |
|
|
300
304
|
| WxApp Mercury Subscribe | `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` | `WXAPP_MERCURY_SUBSCRIBE_FAILED` |
|
|
305
|
+
| WxApp Offer Participant Fields | — | `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` |
|
|
301
306
|
| Upload Logs | `UPLOAD_LOGS_SUCCESS` | `UPLOAD_LOGS_FAILED` |
|
|
302
307
|
| WebSocket Deregister | `WEBSOCKET_DEREGISTER_SUCCESS` | `WEBSOCKET_DEREGISTER_FAIL` |
|
|
303
308
|
| Device Type Update | `AGENT_DEVICE_TYPE_UPDATE_SUCCESS` | `AGENT_DEVICE_TYPE_UPDATE_FAILED` |
|
|
@@ -319,9 +324,48 @@ Webex Together events (`WXAPP_*` constants) use `*_webex_together` compound targ
|
|
|
319
324
|
| Usersub publish | `webex_together_usersub_publish` | `wxcc_sdk.user.webex_together_usersub_publish.complete` | `wxcc_sdk.user.webex_together_usersub_publish.fail` | — |
|
|
320
325
|
| Mercury subscribe | `webex_together_mercury_subscribe` | `wxcc_sdk.user.webex_together_mercury_subscribe.complete` | `wxcc_sdk.user.webex_together_mercury_subscribe.fail` | — |
|
|
321
326
|
| Session init | `webex_together_session_init` | `wxcc_sdk.user.webex_together_session_init.complete` | `wxcc_sdk.user.webex_together_session_init.fail` | `wxcc_sdk.user.webex_together_session_init.ignore` |
|
|
327
|
+
| Offer participant fields | `webex_together_offer_participant_fields` | — | `wxcc_sdk.user.webex_together_offer_participant_fields.fail` | — |
|
|
322
328
|
|
|
323
329
|
Operational and business wire names are unchanged (`WXCC_SDK_WXAPP_*`).
|
|
324
330
|
|
|
331
|
+
### WxApp metric payload fields (operational / behavioral tags)
|
|
332
|
+
|
|
333
|
+
| Event | Payload fields (booleans/enums; no raw IDs) |
|
|
334
|
+
|---|---|
|
|
335
|
+
| `WXAPP_SESSION_INIT_SUCCESS` | `loginOption`, `enableWxBetterTogether`, `usersubPublished`, `mercurySubscribed`, `telephonyTaskType` |
|
|
336
|
+
| `WXAPP_SESSION_INIT_FAILED` | above + `skipReason` (`usersub_not_published`, `mercury_not_subscribed`, `mercury_subscribe_failed`, `publish_failed`) + optional `error`. `usersubPublished` reflects **retained session state** (`isAnswerCallsStateActive()` / `answerCallsState`), including on catch-path failures — not gated on the current init attempt's `publishedEnable`. |
|
|
337
|
+
| `WXAPP_USERSUB_PUBLISH_SUCCESS` | `enableWxBetterTogether`, `usersubPublished` |
|
|
338
|
+
| `WXAPP_USERSUB_PUBLISH_FAILED` | `enableWxBetterTogether`, `usersubPublished`, optional `skipReason`, `error`. `usersubPublished` reflects **retained session state** (`answerCallsState`), not the requested enable value — a failed disable/refresh while usersub remains active reports `true`. |
|
|
339
|
+
| `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` | `mercurySubscribed: true` |
|
|
340
|
+
| `WXAPP_MERCURY_SUBSCRIBE_FAILED` | `mercurySubscribed: false`, `error` |
|
|
341
|
+
| `WXAPP_TASK_ACCEPT_*` / `DECLINE_*` | `taskId`, `hasDeviceCallId`, `hasDeviceId`, optional `acceptReason`; failures add `trackingId` when available. `acceptReason` is captured **before** the telephony REST call (not hard-coded `wxApp_offer_ready`) so success and failure both retain `wxApp_answer_pending` / `wxApp_accept_in_flight` when those flags were already set. Outdial-cancel AQM failures (`error.details`) include the same participant context fields via `getWxAppTelephonyMetricContext` merged with AQM correlation fields. Outdial cancel during post-accept pending phase may report `acceptReason: wxApp_answer_pending`. |
|
|
342
|
+
| `WXAPP_TASK_MUTE_*` / `DTMF_*` | `taskId`, `hasDeviceCallId`, `hasDeviceId`; failures add `trackingId` when available |
|
|
343
|
+
| `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` | `taskId`, `usersubPublished`, `hasDeviceCallId`, `hasDeviceId`, `acceptReason` |
|
|
344
|
+
|
|
345
|
+
### Customer console log triage (MMT / support)
|
|
346
|
+
|
|
347
|
+
Customers often embed with `logger.level: 'log'`. wxApp diagnostics use **`LoggerProxy.log`** / **`warn`** / **`error`** with prefix **`[CC wxApp]`** and grep-friendly `key=value` suffixes on the message string (fields also appear in structured `data`).
|
|
348
|
+
|
|
349
|
+
| Search in browser console | Meaning |
|
|
350
|
+
|---|---|
|
|
351
|
+
| `[CC wxApp]` | All wxApp diagnostic lines (primary search) |
|
|
352
|
+
| `WXCC_SDK_WXAPP` | Operational metric wire names (name only in console; payloads go to backend) |
|
|
353
|
+
| `session readiness` | usersub + Mercury state after station login |
|
|
354
|
+
| `offer decision` | Why Accept is visible/enabled; includes `acceptReason`, `hasDeviceCallId`, `usersubPublished` |
|
|
355
|
+
| `participant fields mismatch` | usersub active but WS participant **still** missing wxApp triple after ~500 ms (transient gaps on first OFFERED tick are normal — look for `wxApp_offer_ready` within 1 s) |
|
|
356
|
+
| `telephony accept failed` | REST answer failure; includes `trackingId` when available |
|
|
357
|
+
|
|
358
|
+
Do **not** search `WXAPP` alone — it matches metric wire names but misses `[CC wxApp]` diagnostic lines.
|
|
359
|
+
|
|
360
|
+
Example lines at `logger.level: 'log'`:
|
|
361
|
+
|
|
362
|
+
```text
|
|
363
|
+
PLUGIN_CC - [LOG]: ... [CC wxApp] session readiness usersubPublished=true mercurySubscribed=true loginOption=EXTENSION ...
|
|
364
|
+
PLUGIN_CC - [LOG]: ... [CC wxApp] offer decision acceptReason=wxApp_offer_ready hasDeviceCallId=true usersubPublished=true interactionId=...
|
|
365
|
+
PLUGIN_CC - [WARN]: ... [CC wxApp] participant fields mismatch usersubPublished=true hasDeviceCallId=false acceptReason=extension_non_wxApp_offer interactionId=...
|
|
366
|
+
PLUGIN_CC - [LOG]: ... operational-events -> @submitEvent. Submit event: WXCC_SDK_WXAPP_TASK_ACCEPT_SUCCESS
|
|
367
|
+
```
|
|
368
|
+
|
|
325
369
|
Special events (no success/failure pair):
|
|
326
370
|
|
|
327
371
|
- `AGENT_RONA` — has behavioral taxonomy (`service.agent_rona.set`)
|
|
@@ -334,11 +378,11 @@ Special events (no success/failure pair):
|
|
|
334
378
|
|
|
335
379
|
- `WXAPP_SESSION_SKIPPED` — has behavioral taxonomy (`wxcc_sdk.user.webex_together_session_init.ignore`)
|
|
336
380
|
|
|
337
|
-
Of the
|
|
381
|
+
Of the 113 defined metric names, 97 have behavioral taxonomy and 16 do not. Events **without** an `eventTaxonomyMap` entry are the original eight `AI_ASSISTANT_*` names, the three Agent Wellness Break `AI_ASSISTANT_WELLNESS_*` names, the two `WELLBEING_BREAK_IDLE_CODE_FETCH_*` names, plus `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, and `WEBSOCKET_EVENT_RECEIVED`. The Agent Wellness Break names are intentionally operational-only and therefore have no `eventTaxonomyMap` entries.
|
|
338
382
|
|
|
339
383
|
### Complete METRIC_EVENT_NAMES catalog
|
|
340
384
|
|
|
341
|
-
This table contains all
|
|
385
|
+
This table contains all 113 names from `src/metrics/constants.ts`; taxonomy presence is checked against `src/metrics/behavioral-events.ts`: 97 mapped and 16 unmapped.
|
|
342
386
|
|
|
343
387
|
| Constant | Emitted name | Behavioral taxonomy? |
|
|
344
388
|
|---|---|---|
|
|
@@ -412,6 +456,7 @@ This table contains all 107 names from `src/metrics/constants.ts`; taxonomy pres
|
|
|
412
456
|
| `WXAPP_USERSUB_PUBLISH_FAILED` | `WxApp Usersub Publish Failed` | yes |
|
|
413
457
|
| `WXAPP_MERCURY_SUBSCRIBE_SUCCESS` | `WxApp Mercury Subscribe Success` | yes |
|
|
414
458
|
| `WXAPP_MERCURY_SUBSCRIBE_FAILED` | `WxApp Mercury Subscribe Failed` | yes |
|
|
459
|
+
| `WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING` | `WxApp Offer Participant Fields Missing` | yes |
|
|
415
460
|
| `UPLOAD_LOGS_SUCCESS` | `Upload Logs Success` | yes |
|
|
416
461
|
| `UPLOAD_LOGS_FAILED` | `Upload Logs Failed` | yes |
|
|
417
462
|
| `WEBSOCKET_DEREGISTER_SUCCESS` | `Websocket Deregister Success` | no |
|
|
@@ -441,6 +486,11 @@ This table contains all 107 names from `src/metrics/constants.ts`; taxonomy pres
|
|
|
441
486
|
| `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED` | `AI Assistant Send Real Time Assistance User Action Failed` | no |
|
|
442
487
|
| `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS` | `AI Assistant Fetch Historic Transcripts Success` | no |
|
|
443
488
|
| `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED` | `AI Assistant Fetch Historic Transcripts Failed` | no |
|
|
489
|
+
| `AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED` | `AI Assistant Wellness Action Accepted` | no |
|
|
490
|
+
| `AI_ASSISTANT_WELLNESS_ACTION_FAILED` | `AI Assistant Wellness Action Failed` | no |
|
|
491
|
+
| `AI_ASSISTANT_WELLNESS_EVENT_INVALID` | `AI Assistant Wellness Event Invalid` | no |
|
|
492
|
+
| `WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS` | `Wellbeing Break Idle Code Fetch Success` | no |
|
|
493
|
+
| `WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED` | `Wellbeing Break Idle Code Fetch Failed` | no |
|
|
444
494
|
| `USER_PREFERENCE_GET_SUCCESS` | `User Preference Get Success` | yes |
|
|
445
495
|
| `USER_PREFERENCE_GET_FAILED` | `User Preference Get Failed` | yes |
|
|
446
496
|
| `USER_PREFERENCE_CREATE_SUCCESS` | `User Preference Create Success` | yes |
|
|
@@ -450,7 +500,7 @@ This table contains all 107 names from `src/metrics/constants.ts`; taxonomy pres
|
|
|
450
500
|
| `USER_PREFERENCE_DELETE_SUCCESS` | `User Preference Delete Success` | yes |
|
|
451
501
|
| `USER_PREFERENCE_DELETE_FAILED` | `User Preference Delete Failed` | yes |
|
|
452
502
|
|
|
453
|
-
Defined names without an `eventTaxonomyMap` entry: `
|
|
503
|
+
Defined names without an `eventTaxonomyMap` entry: `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_EVENT_RECEIVED`, all eight AI Assistant request names, and the seven Agent Wellness Break notification/action/system-code/ASC names listed above.
|
|
454
504
|
|
|
455
505
|
## Requires (dependencies)
|
|
456
506
|
- `webex.internal.newMetrics` submission APIs
|
|
@@ -473,6 +523,7 @@ Defined names without an `eventTaxonomyMap` entry: `AI_ASSISTANT_FETCH_HISTORIC_
|
|
|
473
523
|
| METRICS-R-003 | When metrics are disabled, clear pending events and make `timeEvent` plus all tracking methods return without recording/submitting. | Telemetry must never block or alter product behavior and disablement must be comprehensive. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
474
524
|
| METRICS-R-004 | Queue submissions until the host SDK is ready and flush through the correct behavioral/operational/business service. | Early lifecycle telemetry must not be lost solely because the host is not ready. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
475
525
|
| METRICS-R-005 | Submit through the host SDK's `webex.internal.newMetrics` client without storing credentials or implementing authorization policy in MetricsManager. | Host-owned authentication keeps telemetry credential handling outside the Contact Center metrics module. | `src/metrics/MetricsManager.ts` | `test/unit/spec/metrics/MetricsManager.ts` | None; authentication is inherited and credential ownership is explicitly N/A. | PRESENT |
|
|
526
|
+
| METRICS-R-006 | Track wellness action, invalid notification, system-code lookup, and ASC state-change outcomes as operational metrics using fixed names and reason-only/context-minimized payloads. | The feature needs diagnosable lifecycle signals without logging wellness payloads or agent PII. | `src/metrics/constants.ts`, `src/cc.ts`, `src/services/ApiAiAssistant.ts` | `test/unit/spec/cc.ts`, `test/unit/spec/services/ApiAiAssistant.ts` | These names intentionally have no behavioral taxonomy. | PRESENT |
|
|
476
527
|
|
|
477
528
|
## Design Overview
|
|
478
529
|
Metrics separates its stable consumption boundary from collaborators so ownership and failure behavior stay explicit. Telemetry is deliberately non-blocking and queue-backed so product behavior never waits for metrics; failures are logged rather than propagated.
|
|
@@ -495,18 +546,7 @@ Each behavioral event maps to a structured taxonomy in `behavioral-events.ts`:
|
|
|
495
546
|
|
|
496
547
|
**Example**: `STATION_LOGIN_SUCCESS` maps to `wxcc_sdk.user.station_login.complete`
|
|
497
548
|
|
|
498
|
-
> **Note**: The
|
|
499
|
-
> - `AI_ASSISTANT_SEND_EVENT_SUCCESS`
|
|
500
|
-
> - `AI_ASSISTANT_SEND_EVENT_FAILED`
|
|
501
|
-
> - `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_SUCCESS`
|
|
502
|
-
> - `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_FAILED`
|
|
503
|
-
> - `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_SUCCESS`
|
|
504
|
-
> - `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED`
|
|
505
|
-
> - `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS`
|
|
506
|
-
> - `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED`
|
|
507
|
-
> - `WEBSOCKET_DEREGISTER_SUCCESS`
|
|
508
|
-
> - `WEBSOCKET_DEREGISTER_FAIL`
|
|
509
|
-
> - `WEBSOCKET_EVENT_RECEIVED`
|
|
549
|
+
> **Note**: The 20 names catalogued above as lacking `eventTaxonomyMap` entries, including all nine new wellness-related names, are operational-only.
|
|
510
550
|
>
|
|
511
551
|
> Calling `trackBehavioralEvent` with these event names will push an event with an `undefined` taxonomy.
|
|
512
552
|
|
|
@@ -796,7 +836,7 @@ MetricsManager holds three pending queues, running timing entries, a readiness f
|
|
|
796
836
|
|
|
797
837
|
## Business Rules & Invariants
|
|
798
838
|
- `timeEvent` and every tracking method return immediately while metrics are disabled.
|
|
799
|
-
- Only names present in `eventTaxonomyMap` receive behavioral taxonomy;
|
|
839
|
+
- Only names present in `eventTaxonomyMap` receive behavioral taxonomy; 81 of 101 names are mapped and all 20 exceptions are explicitly catalogued as unmapped.
|
|
800
840
|
- `submittingEvents` prevents overlapping queue-drain executions.
|
|
801
841
|
- Authentication is inherited from the host Webex SDK's metrics client; MetricsManager owns no credentials, tokens, or authorization policy.
|
|
802
842
|
|
|
@@ -831,7 +871,7 @@ stateDiagram-v2
|
|
|
831
871
|
- **metricsDisabled**: When `true`, `timeEvent` and all `track*` methods return early, and `clearPendingEvents()` empties all queues.
|
|
832
872
|
|
|
833
873
|
## Pitfalls
|
|
834
|
-
- `METRIC_EVENT_NAMES` and `eventTaxonomyMap` are different inventories:
|
|
874
|
+
- `METRIC_EVENT_NAMES` and `eventTaxonomyMap` are different inventories: 20 defined names intentionally have no behavioral taxonomy.
|
|
835
875
|
- `setMetricsDisabled(true)` clears pending queues but does not create a delivery receipt; callers must not infer that previously submitted events were accepted.
|
|
836
876
|
- Submission helpers hand events to `webex.internal.newMetrics` without a module-level retry/requeue policy, so telemetry must remain non-blocking and non-authoritative.
|
|
837
877
|
|
|
@@ -892,10 +932,11 @@ Use `test/unit/spec/metrics/MetricsManager.ts` for readiness queues, timing/trac
|
|
|
892
932
|
| Behavior / Requirement | Existing test evidence | Gap |
|
|
893
933
|
|---|---|---|
|
|
894
934
|
| `METRICS-R-001` | `test/unit/spec/metrics/MetricsManager.ts` | Add a catalog parity assertion if constants change. |
|
|
895
|
-
| `METRICS-R-002` | `test/unit/spec/metrics/behavioral-events.ts` | Keep explicit coverage for all
|
|
935
|
+
| `METRICS-R-002` | `test/unit/spec/metrics/behavioral-events.ts` | Keep explicit coverage for all 20 unmapped names. |
|
|
896
936
|
| `METRICS-R-003` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
|
|
897
937
|
| `METRICS-R-004` | `test/unit/spec/metrics/MetricsManager.ts` | None. |
|
|
898
938
|
| `METRICS-R-005` | `test/unit/spec/metrics/MetricsManager.ts` | Authentication ownership is verified indirectly through the host metrics client. |
|
|
939
|
+
| `METRICS-R-006` | `test/unit/spec/cc.ts`, `test/unit/spec/services/ApiAiAssistant.ts` | Add direct catalog-parity coverage if metrics constants receive a dedicated test. |
|
|
899
940
|
|
|
900
941
|
## Traceability
|
|
901
942
|
- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../ai-docs/SPEC_INDEX.md`
|
|
@@ -405,6 +405,12 @@ const eventTaxonomyMap: Record<string, BehavioralEventTaxonomy> = {
|
|
|
405
405
|
target: 'webex_together_mercury_subscribe',
|
|
406
406
|
verb: 'fail',
|
|
407
407
|
},
|
|
408
|
+
[METRIC_EVENT_NAMES.WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING]: {
|
|
409
|
+
product,
|
|
410
|
+
agent: 'user',
|
|
411
|
+
target: 'webex_together_offer_participant_fields',
|
|
412
|
+
verb: 'fail',
|
|
413
|
+
},
|
|
408
414
|
|
|
409
415
|
// Conference Tasks
|
|
410
416
|
[METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS]: {
|
package/src/metrics/constants.ts
CHANGED
|
@@ -156,6 +156,7 @@ export const METRIC_EVENT_NAMES = {
|
|
|
156
156
|
WXAPP_USERSUB_PUBLISH_FAILED: 'WxApp Usersub Publish Failed',
|
|
157
157
|
WXAPP_MERCURY_SUBSCRIBE_SUCCESS: 'WxApp Mercury Subscribe Success',
|
|
158
158
|
WXAPP_MERCURY_SUBSCRIBE_FAILED: 'WxApp Mercury Subscribe Failed',
|
|
159
|
+
WXAPP_OFFER_PARTICIPANT_FIELDS_MISSING: 'WxApp Offer Participant Fields Missing',
|
|
159
160
|
|
|
160
161
|
UPLOAD_LOGS_SUCCESS: 'Upload Logs Success',
|
|
161
162
|
UPLOAD_LOGS_FAILED: 'Upload Logs Failed',
|
|
@@ -203,6 +204,11 @@ export const METRIC_EVENT_NAMES = {
|
|
|
203
204
|
AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS:
|
|
204
205
|
'AI Assistant Fetch Historic Transcripts Success',
|
|
205
206
|
AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED: 'AI Assistant Fetch Historic Transcripts Failed',
|
|
207
|
+
AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED: 'AI Assistant Wellness Action Accepted',
|
|
208
|
+
AI_ASSISTANT_WELLNESS_ACTION_FAILED: 'AI Assistant Wellness Action Failed',
|
|
209
|
+
AI_ASSISTANT_WELLNESS_EVENT_INVALID: 'AI Assistant Wellness Event Invalid',
|
|
210
|
+
WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS: 'Wellbeing Break Idle Code Fetch Success',
|
|
211
|
+
WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED: 'Wellbeing Break Idle Code Fetch Failed',
|
|
206
212
|
|
|
207
213
|
// User Preference API Events
|
|
208
214
|
USER_PREFERENCE_GET_SUCCESS: 'User Preference Get Success',
|
|
@@ -97,7 +97,7 @@ export default class AnswerCallOnWebexService {
|
|
|
97
97
|
try {
|
|
98
98
|
const uri = `${this.getTelephonyBaseUrl()}${resource}`;
|
|
99
99
|
|
|
100
|
-
LoggerProxy.
|
|
100
|
+
LoggerProxy.log(`AnswerCallOnWebexService.${logMethod} request`, {
|
|
101
101
|
module: ANSWER_CALL_ON_WEBEX_FILE,
|
|
102
102
|
method: logMethod,
|
|
103
103
|
data: {uri, bodyKeys: Object.keys(body)},
|
|
@@ -110,7 +110,7 @@ export default class AnswerCallOnWebexService {
|
|
|
110
110
|
addAuthHeader: true,
|
|
111
111
|
})) as IHttpResponse;
|
|
112
112
|
|
|
113
|
-
LoggerProxy.
|
|
113
|
+
LoggerProxy.log(`AnswerCallOnWebexService.${logMethod} success`, {
|
|
114
114
|
module: ANSWER_CALL_ON_WEBEX_FILE,
|
|
115
115
|
method: logMethod,
|
|
116
116
|
data: {statusCode: response.statusCode},
|
|
@@ -197,10 +197,10 @@ export default class AnswerCallOnWebexService {
|
|
|
197
197
|
const uri = `${this.getTelephonyBaseUrl()}/${callId}${lineOwnerIdQuery}`;
|
|
198
198
|
|
|
199
199
|
try {
|
|
200
|
-
LoggerProxy.
|
|
200
|
+
LoggerProxy.log('AnswerCallOnWebexService.getCallDetails request', {
|
|
201
201
|
module: ANSWER_CALL_ON_WEBEX_FILE,
|
|
202
202
|
method: METHODS.GET_CALL_DETAILS_ON_WEBEX,
|
|
203
|
-
data: {callId},
|
|
203
|
+
data: {callIdSuffix: callId.length <= 8 ? callId : callId.slice(-8)},
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
const response = (await this.webex.request({
|
|
@@ -12,6 +12,10 @@ import {
|
|
|
12
12
|
HistoricTranscriptsResponse,
|
|
13
13
|
RealTimeAssistanceParams,
|
|
14
14
|
RealTimeAssistanceUserActionParams,
|
|
15
|
+
RespondToWellnessBreakParams,
|
|
16
|
+
WellnessBreakUserAction,
|
|
17
|
+
WELLNESS_BREAK_USER_ACTIONS,
|
|
18
|
+
GenericError,
|
|
15
19
|
} from '../types';
|
|
16
20
|
import {getErrorDetails} from './core/Utils';
|
|
17
21
|
import {
|
|
@@ -22,6 +26,12 @@ import {
|
|
|
22
26
|
} from './constants';
|
|
23
27
|
import {AIFeatureFlags} from './config/types';
|
|
24
28
|
|
|
29
|
+
type WellnessBreakContextProvider = () => {
|
|
30
|
+
isWellnessBreakEnabled: boolean;
|
|
31
|
+
agentId?: string;
|
|
32
|
+
agentSessionId?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
25
35
|
/**
|
|
26
36
|
* ApiAIAssistant provides AI Assistant APIs for transcript controls.
|
|
27
37
|
* @public
|
|
@@ -30,9 +40,23 @@ export class ApiAIAssistant {
|
|
|
30
40
|
private webex: WebexSDK;
|
|
31
41
|
private metricsManager: MetricsManager;
|
|
32
42
|
private aiFeature: AIFeatureFlags;
|
|
43
|
+
private readonly wellnessContextProvider?: WellnessBreakContextProvider;
|
|
44
|
+
|
|
45
|
+
private createWellnessError(reason: string): GenericError {
|
|
46
|
+
const error = new Error(reason) as GenericError;
|
|
47
|
+
error.details = {
|
|
48
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
49
|
+
orgId: this.webex.credentials.getOrgId(),
|
|
50
|
+
trackingId: '',
|
|
51
|
+
data: {reason},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return error;
|
|
55
|
+
}
|
|
33
56
|
|
|
34
|
-
constructor(webex: WebexSDK) {
|
|
57
|
+
constructor(webex: WebexSDK, wellnessContextProvider?: WellnessBreakContextProvider) {
|
|
35
58
|
this.webex = webex;
|
|
59
|
+
this.wellnessContextProvider = wellnessContextProvider;
|
|
36
60
|
this.metricsManager = MetricsManager.getInstance({webex});
|
|
37
61
|
}
|
|
38
62
|
|
|
@@ -40,6 +64,134 @@ export class ApiAIAssistant {
|
|
|
40
64
|
this.aiFeature = aiFeature;
|
|
41
65
|
}
|
|
42
66
|
|
|
67
|
+
private async sendWellnessBreakAction(
|
|
68
|
+
action: WellnessBreakUserAction,
|
|
69
|
+
method: string
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
this.metricsManager.timeEvent([
|
|
72
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED,
|
|
73
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_ACTION_FAILED,
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const context = this.wellnessContextProvider?.();
|
|
78
|
+
const agentId = context?.agentId?.trim();
|
|
79
|
+
const agentSessionId = context?.agentSessionId?.trim();
|
|
80
|
+
const orgId = this.webex.credentials.getOrgId()?.trim();
|
|
81
|
+
|
|
82
|
+
if (context?.isWellnessBreakEnabled !== true) {
|
|
83
|
+
throw this.createWellnessError('WELLNESS_BREAK_NOT_ENABLED');
|
|
84
|
+
}
|
|
85
|
+
if (!orgId) {
|
|
86
|
+
throw this.createWellnessError('WELLNESS_BREAK_ORG_ID_REQUIRED');
|
|
87
|
+
}
|
|
88
|
+
if (!agentId) {
|
|
89
|
+
throw this.createWellnessError('WELLNESS_BREAK_AGENT_ID_REQUIRED');
|
|
90
|
+
}
|
|
91
|
+
if (!agentSessionId) {
|
|
92
|
+
throw this.createWellnessError('WELLNESS_BREAK_AGENT_SESSION_REQUIRED');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const baseUrl = this.getBaseUrl();
|
|
96
|
+
const response = (await this.webex.request({
|
|
97
|
+
uri: `${baseUrl}${AI_ASSISTANT_API_URLS.EVENT}`,
|
|
98
|
+
method: HTTP_METHODS.POST,
|
|
99
|
+
addAuthHeader: true,
|
|
100
|
+
body: {
|
|
101
|
+
agentId,
|
|
102
|
+
orgId,
|
|
103
|
+
eventType: AIAssistantEventType.CUSTOM_EVENT,
|
|
104
|
+
eventName: AIAssistantEventName.WELLNESS_BREAK_ACTION,
|
|
105
|
+
eventDetails: {
|
|
106
|
+
data: {
|
|
107
|
+
action,
|
|
108
|
+
agentSessionId,
|
|
109
|
+
actionTimeStamp: Date.now(),
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
})) as IHttpResponse;
|
|
114
|
+
|
|
115
|
+
if (response.statusCode !== 202) {
|
|
116
|
+
throw this.createWellnessError(
|
|
117
|
+
`WELLNESS_BREAK_ACTION_UNEXPECTED_STATUS_${response.statusCode}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.metricsManager.trackEvent(
|
|
122
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_ACTION_ACCEPTED,
|
|
123
|
+
{action},
|
|
124
|
+
['operational']
|
|
125
|
+
);
|
|
126
|
+
LoggerProxy.log('Wellness action accepted by AI Assistant', {
|
|
127
|
+
module: CC_FILE,
|
|
128
|
+
method,
|
|
129
|
+
data: {action},
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
this.metricsManager.trackEvent(
|
|
133
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_ACTION_FAILED,
|
|
134
|
+
{
|
|
135
|
+
action,
|
|
136
|
+
error: error instanceof Error ? error.message : String(error),
|
|
137
|
+
},
|
|
138
|
+
['operational']
|
|
139
|
+
);
|
|
140
|
+
const {error: detailedError} = getErrorDetails(error, method, CC_FILE);
|
|
141
|
+
throw detailedError;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Requests an Agent Wellness Break.
|
|
147
|
+
* The promise resolves when the HTTP request is accepted with status 202; the later
|
|
148
|
+
* approval or denial is delivered independently through a wellness notification.
|
|
149
|
+
* @returns A promise that resolves with no value after HTTP acceptance
|
|
150
|
+
* @throws Structured Contact Center error when disabled, stale, invalid, or delivery fails
|
|
151
|
+
* @example
|
|
152
|
+
* await webex.cc.apiAIAssistant.requestWellnessBreak();
|
|
153
|
+
* @public
|
|
154
|
+
*/
|
|
155
|
+
public async requestWellnessBreak(): Promise<void> {
|
|
156
|
+
return this.sendWellnessBreakAction(
|
|
157
|
+
WELLNESS_BREAK_USER_ACTIONS.REQUESTED,
|
|
158
|
+
METHODS.REQUEST_WELLNESS_BREAK
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Responds to a backend-provided Agent Wellness Break offer.
|
|
164
|
+
* The promise resolves when the HTTP request is accepted with status 202 and does not
|
|
165
|
+
* wait for a WebSocket completion event.
|
|
166
|
+
* @param params - ACCEPTED, REJECTED, or NO_RESPONSE
|
|
167
|
+
* @returns A promise that resolves with no value after HTTP acceptance
|
|
168
|
+
* @throws Structured Contact Center error when disabled, stale, invalid, or delivery fails
|
|
169
|
+
* @example
|
|
170
|
+
* await webex.cc.apiAIAssistant.respondToWellnessBreak({
|
|
171
|
+
* action: WELLNESS_BREAK_USER_ACTIONS.ACCEPTED,
|
|
172
|
+
* });
|
|
173
|
+
* @public
|
|
174
|
+
*/
|
|
175
|
+
public async respondToWellnessBreak(params: RespondToWellnessBreakParams): Promise<void> {
|
|
176
|
+
const {action} = params;
|
|
177
|
+
if (
|
|
178
|
+
![
|
|
179
|
+
WELLNESS_BREAK_USER_ACTIONS.ACCEPTED,
|
|
180
|
+
WELLNESS_BREAK_USER_ACTIONS.REJECTED,
|
|
181
|
+
WELLNESS_BREAK_USER_ACTIONS.NO_RESPONSE,
|
|
182
|
+
].includes(action)
|
|
183
|
+
) {
|
|
184
|
+
const {error} = getErrorDetails(
|
|
185
|
+
this.createWellnessError('WELLNESS_BREAK_ACTION_INVALID'),
|
|
186
|
+
METHODS.RESPOND_TO_WELLNESS_BREAK,
|
|
187
|
+
CC_FILE
|
|
188
|
+
);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return this.sendWellnessBreakAction(action, METHODS.RESPOND_TO_WELLNESS_BREAK);
|
|
193
|
+
}
|
|
194
|
+
|
|
43
195
|
private getBaseUrl(): string {
|
|
44
196
|
const wccApiGatewayUrl = this.webex.internal.services.get(WCC_API_GATEWAY) || '';
|
|
45
197
|
|
|
@@ -213,6 +213,7 @@ export default class WebexCrossClientService {
|
|
|
213
213
|
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
214
214
|
{
|
|
215
215
|
enableWxBetterTogether: enable,
|
|
216
|
+
usersubPublished: this.answerCallsState,
|
|
216
217
|
error: error instanceof Error ? error.toString() : String(error),
|
|
217
218
|
},
|
|
218
219
|
['operational', 'behavioral']
|
|
@@ -243,12 +244,15 @@ export default class WebexCrossClientService {
|
|
|
243
244
|
if (trackPublishMetrics) {
|
|
244
245
|
metricsManager.trackEvent(
|
|
245
246
|
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_SUCCESS,
|
|
246
|
-
{
|
|
247
|
+
{
|
|
248
|
+
enableWxBetterTogether: enable,
|
|
249
|
+
usersubPublished: enable,
|
|
250
|
+
},
|
|
247
251
|
['operational', 'behavioral']
|
|
248
252
|
);
|
|
249
253
|
}
|
|
250
254
|
|
|
251
|
-
LoggerProxy.
|
|
255
|
+
LoggerProxy.log(`Cross-client answer-calls-on-wxcc set to ${enable}`, {
|
|
252
256
|
module: WEBEX_CROSS_CLIENT_FILE,
|
|
253
257
|
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
254
258
|
data: {
|
|
@@ -273,6 +277,7 @@ export default class WebexCrossClientService {
|
|
|
273
277
|
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
274
278
|
{
|
|
275
279
|
enableWxBetterTogether: enable,
|
|
280
|
+
usersubPublished: this.answerCallsState,
|
|
276
281
|
skipReason,
|
|
277
282
|
error,
|
|
278
283
|
},
|
|
@@ -88,7 +88,7 @@ export default class WxAppTelephonyMercurySync {
|
|
|
88
88
|
|
|
89
89
|
MetricsManager.getInstance().trackEvent(
|
|
90
90
|
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
91
|
-
{error: 'Mercury is unavailable for wxApp mute sync'},
|
|
91
|
+
{error: 'Mercury is unavailable for wxApp mute sync', mercurySubscribed: false},
|
|
92
92
|
['operational', 'behavioral']
|
|
93
93
|
);
|
|
94
94
|
|
|
@@ -110,11 +110,11 @@ export default class WxAppTelephonyMercurySync {
|
|
|
110
110
|
|
|
111
111
|
MetricsManager.getInstance().trackEvent(
|
|
112
112
|
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_SUCCESS,
|
|
113
|
-
{},
|
|
113
|
+
{mercurySubscribed: true},
|
|
114
114
|
['operational', 'behavioral']
|
|
115
115
|
);
|
|
116
116
|
|
|
117
|
-
LoggerProxy.
|
|
117
|
+
LoggerProxy.log('Subscribed to wxApp telephony Mercury mute sync', {
|
|
118
118
|
module: WXAPP_TELEPHONY_MERCURY_SYNC_FILE,
|
|
119
119
|
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
120
120
|
});
|
|
@@ -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 /
|
|
15
|
-
| Validation status |
|
|
14
|
+
| generated_by / approved_by / updated_at | Codex generator / Agent Wellness Break v0.4 intake audit / 2026-09-07 |
|
|
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.
|
|
@@ -55,6 +55,7 @@ The `src/services/` directory is the service layer of the `@webex/contact-center
|
|
|
55
55
|
| **Utilities** | [`src/utils/PageCache.ts`](../../utils/PageCache.ts) | Shared `PageCache<T>` generic class for pagination caching, plus `BaseSearchParams`, `PaginatedResponse`, and `PaginationMeta` types used by all data services |
|
|
56
56
|
| **WebRTC Calling** | [`WebCallingService.ts`](../WebCallingService.ts) | Browser-based voice calling via `@webex/calling`, line registration, call answer/mute/decline |
|
|
57
57
|
| **WxApp Better Together** | [`WebexCrossClientService.ts`](../WebexCrossClientService.ts), [`AnswerCallOnWebexService.ts`](../AnswerCallOnWebexService.ts), [`WxAppTelephonyMercurySync.ts`](../WxAppTelephonyMercurySync.ts) | usersub cross-client publish (`answer-calls-on-wxcc`), Hydra telephony REST (answer/reject/mute/DTMF/GET call), Mercury mute sync — orchestrated from `cc.ts` when `enableWxBetterTogether` is active |
|
|
58
|
+
| **AI Assistant / Wellness** | [`ApiAiAssistant.ts`](../ApiAiAssistant.ts) | Existing transcript/suggestion requests plus direct, session-validated wellness custom-event reporting. |
|
|
58
59
|
|
|
59
60
|
Each service folder contains its own `ai-docs/` with detailed documentation. **Always load the relevant service docs before making changes.**
|
|
60
61
|
|
|
@@ -230,6 +231,7 @@ Use [`constants.ts`](../constants.ts) as the canonical source for service-level
|
|
|
230
231
|
| `services.surface` | SDK / event / internal API | Internal `Services.getInstance()` composition root plus data-service and calling collaborators consumed by `ContactCenter`. | Stable module consumption boundary. | Additive changes by default; breaking package exports require a major-version transition. | `src/services/index.ts` | `../../../ai-docs/CONTRACTS.md` |
|
|
231
232
|
| `services.destination-lists` | SDK data API | Existing `Queue.getQueues(ContactServiceQueueSearchParams)` and `EntryPoint.getEntryPoints(EntryPointSearchParams)` methods with full-record paginated responses. | Apply consult/transfer telephony eligibility, profile views, and backend name ordering as overridable defaults. | Behavioral default correction on existing methods; no specialized method, projected response, or replacement signature. | `src/services/Queue.ts`, `src/services/EntryPoint.ts`, `src/types.ts` | `../../../ai-docs/CONTRACTS.md` |
|
|
232
233
|
| `services.user-preference` | SDK data API | `UserPreference` and `cc.userPreference`. | Direct REST CRUD for user preferences; separate from PageCache-based pagination services. | Additive semver-public API; removals or signature changes are breaking. | `src/services/UserPreference.ts`, `src/services/config/types.ts` | `../../../ai-docs/CONTRACTS.md` |
|
|
234
|
+
| `services.agent-wellness-break` | SDK data / action API | `ApiAIAssistant.requestWellnessBreak` and `respondToWellnessBreak`, invoked through `ContactCenter`. | Submit validated `WellnessBreakAction` custom events through direct HTTP. | Additive semver-public behavior; exact action/event payload is backend-owned. | `src/services/ApiAiAssistant.ts`, `src/types.ts` | `../../../ai-docs/CONTRACTS.md` |
|
|
233
235
|
|
|
234
236
|
Compatibility notes:
|
|
235
237
|
- Do not remove or reinterpret exported symbols/events without a documented consumer migration.
|
|
@@ -264,6 +266,7 @@ ContactCenter READY callback
|
|
|
264
266
|
| SERVICES-R-005 | Inherit authenticated request identity from the host Webex SDK through Core/WebexRequest; Services must not store, parse, or refresh credentials. | One host-owned authentication boundary avoids duplicate token handling and credential leakage across composed services. | `src/services/index.ts`, `src/services/core/WebexRequest.ts` | `test/unit/spec/services/core/WebexRequest.ts` | None; authentication ownership is explicit. | PRESENT |
|
|
265
267
|
| SERVICES-R-006 | Treat Services composition as unconditionally created by the ContactCenter READY callback; Services owns no rollout or feature-flag decision. | Capability flags belong to the consuming config/task/calling collaborators, so the composition root must not silently gate construction. | `src/services/index.ts`, `src/cc.ts` | `test/unit/spec/cc.ts` | None; rollout applicability is explicitly N/A for Services. | PRESENT |
|
|
266
268
|
| SERVICES-R-007 | Existing Queue and EntryPoint list methods must apply inbound, active, telephony, profile/agent-view, and `name,ASC` defaults while retaining their established parameter and full-record response types. Caller-supplied existing filter, sort, or profile inputs override defaults. Queue must also treat `sortOrder` without `sortBy` as a name sort and bypass the simple-page cache. | Defaults on the established methods let thin consumers request lists without a parallel API, while existing parameters preserve specialized behavior for other consumers and full-record types remain truthful because no field projection is requested. | `src/services/Queue.ts`, `src/services/EntryPoint.ts`, `src/types.ts` | `test/unit/spec/services/Queue.ts`, `test/unit/spec/services/EntryPoint.ts`, `test/unit/spec/cc.ts` | The backend honors the view flags and combined CMS sort value. | PRESENT |
|
|
269
|
+
| SERVICES-R-008 | ApiAIAssistant wellness requests must accept no public identity or context-mutating input, read effective enablement and non-empty SDK-owned organization/agent/session context from a read-only provider supplied at construction, then POST the exact `WellnessBreakAction` custom-event body directly to `/event`; only HTTP 202 resolves successfully. Public action unions derive from exported `as const` action-value objects. | SDK-owned live context prevents callers from supplying stale/different login identities to action calls, one value/type source prevents wire drift, and direct HTTP matches the AI event endpoint rather than AQM completion semantics. | `src/services/ApiAiAssistant.ts`, `src/types.ts`, `src/index.ts`, `src/cc.ts` | `test/unit/spec/services/ApiAiAssistant.ts`, `test/unit/spec/cc.ts` | None. | PRESENT |
|
|
267
270
|
|
|
268
271
|
## Design Overview
|
|
269
272
|
`Services` is a singleton composition root for transport-facing capabilities only. It constructs two `WebSocketManager` instances (primary Contact Center and RTD), creates `AqmReqs` on the primary manager, then creates config, agent, contact, dialer, and ConnectionService collaborators.
|
|
@@ -272,6 +275,8 @@ ContactCenter owns the broader READY-time graph: WebCallingService, ApiAIAssista
|
|
|
272
275
|
|
|
273
276
|
AQM factories return functions whose HTTP request is initiation and whose promise settles on correlated primary-WebSocket notifications. Direct config/data services return authenticated REST responses.
|
|
274
277
|
|
|
278
|
+
ApiAIAssistant wellness reporting is also direct REST, but it reads live context through a read-only provider supplied by ContactCenter at construction rather than a separate factory or context setter. It fails closed unless wellness is effectively enabled and the SDK owns a current organization, agent, and session; public action calls do not accept identity fields.
|
|
279
|
+
|
|
275
280
|
## Data Flow
|
|
276
281
|
```mermaid
|
|
277
282
|
flowchart TD
|
|
@@ -399,6 +404,7 @@ classDiagram
|
|
|
399
404
|
- **UC-2 Direct REST:** configuration, data, and user-preference services return authenticated HTTP results directly. Evidence: `src/services/config/index.ts`, `src/services/UserPreference.ts`, `test/unit/spec/services/config/index.ts`, `test/unit/spec/services/UserPreference.ts`.
|
|
400
405
|
- **UC-3 AQM operation:** initiate HTTP and settle only on matching WebSocket notification or timeout. Evidence: `src/services/core/aqm-reqs.ts`, `test/unit/spec/services/core/aqm-reqs.ts`.
|
|
401
406
|
- **UC-4 Task/AI realtime:** TaskManager consumes primary and RTD streams with ApiAIAssistant/calling collaborators. Evidence: `src/services/task/TaskManager.ts`, `test/unit/spec/services/task/TaskManager.ts`.
|
|
407
|
+
- **UC-5 Wellness action reporting:** validate the current profile/session context and submit a direct AI custom event that completes only on HTTP 202. Evidence: `src/services/ApiAiAssistant.ts`, `test/unit/spec/services/ApiAiAssistant.ts`.
|
|
402
408
|
|
|
403
409
|
## State Model
|
|
404
410
|
`Services` is a process-local singleton. Its first `getInstance({webex, connectionConfig})` call synchronously constructs the two WebSocket managers, AqmReqs-backed factories, config service, and ConnectionService; later calls return that same composed graph. Socket lifecycle and domain records are owned by the corresponding collaborators, not by a Services state machine.
|
|
@@ -409,6 +415,7 @@ classDiagram
|
|
|
409
415
|
- ApiAIAssistant, TaskManager, and UserPreference are READY-time ContactCenter collaborators, not fields constructed by Services.
|
|
410
416
|
- Authentication is inherited from the host SDK through Core/WebexRequest; Services owns no credential lifecycle.
|
|
411
417
|
- Rollout applicability is N/A for the Services composition root: it is created at READY and does not evaluate a feature flag.
|
|
418
|
+
- ApiAIAssistant evaluates the current effective wellness flag and identities through its constructor-supplied read-only provider; the public object exposes no context mutator, and the Services singleton itself remains unconditionally composed.
|
|
412
419
|
|
|
413
420
|
## Concurrency & Reactive Flow
|
|
414
421
|
- `getInstance` composition is synchronous in the JavaScript execution turn. Direct REST promises can proceed independently, while each AQM promise remains pending until its matching primary-WebSocket notification, HTTP failure, or timeout.
|
|
@@ -447,7 +454,7 @@ stateDiagram-v2
|
|
|
447
454
|
```
|
|
448
455
|
|
|
449
456
|
## Protocol / Wire Format
|
|
450
|
-
- Request, response, and event payload ownership is anchored in `src/services/index.ts`. HTTP initiates backend work where applicable; WebSocket messages provide realtime events and, for AQM flows, correlated completion.
|
|
457
|
+
- Request, response, and event payload ownership is anchored in `src/services/index.ts`. HTTP initiates backend work where applicable; WebSocket messages provide realtime events and, for AQM flows, correlated completion. Wellness actions POST `CUSTOM_EVENT` / `WellnessBreakAction` bodies directly to `/event` and accept only status 202; wellness offers arrive as primary `Wellness_Break_Handler` notifications.
|
|
451
458
|
|
|
452
459
|
## Error Handling & Failure Modes
|
|
453
460
|
| Condition | Signal (error/code/result) | Caller recovery |
|
|
@@ -481,6 +488,7 @@ Unit tests mirror module paths under `test/unit/spec/services`. Preserve positiv
|
|
|
481
488
|
| `SERVICES-R-005` | `test/unit/spec/services/core/WebexRequest.ts` | None. |
|
|
482
489
|
| `SERVICES-R-006` | `test/unit/spec/cc.ts` | None. |
|
|
483
490
|
| `SERVICES-R-007` | `test/unit/spec/services/Queue.ts`, `test/unit/spec/services/EntryPoint.ts`, `test/unit/spec/cc.ts` | None. |
|
|
491
|
+
| `SERVICES-R-008` | `test/unit/spec/services/ApiAiAssistant.ts` | None. |
|
|
484
492
|
|
|
485
493
|
## Traceability
|
|
486
494
|
- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../ai-docs/SPEC_INDEX.md`
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {LOST_CONNECTION_RECOVERY_TIMEOUT} from '../core/constants';
|
|
2
|
+
import {WELLNESS_BREAK_REMINDERS_ENABLED} from './constants';
|
|
2
3
|
import {
|
|
3
4
|
AgentResponse,
|
|
4
5
|
AuxCode,
|
|
@@ -186,6 +187,10 @@ function parseAgentConfigs(profileData: {
|
|
|
186
187
|
const defaultWrapUpData = getDefaultWrapUpCode(wrapupCodes);
|
|
187
188
|
const aiFeature: AIFeatureFlags | undefined =
|
|
188
189
|
aiFeatureFlags?.data?.length > 0 ? aiFeatureFlags.data[0] : undefined;
|
|
190
|
+
const isWellnessConfigured =
|
|
191
|
+
aiFeature?.agentWellbeing?.enable === true &&
|
|
192
|
+
aiFeature.agentWellbeing.wellnessBreakReminders === WELLNESS_BREAK_REMINDERS_ENABLED;
|
|
193
|
+
const hasAIAssistantLicense = (orgSettingsData.aiAssistantQuantity ?? 0) > 0;
|
|
189
194
|
|
|
190
195
|
const finalData = {
|
|
191
196
|
teams: teamData,
|
|
@@ -264,6 +269,7 @@ function parseAgentConfigs(profileData: {
|
|
|
264
269
|
lostConnectionRecoveryTimeout:
|
|
265
270
|
tenantData.lostConnectionRecoveryTimeout || LOST_CONNECTION_RECOVERY_TIMEOUT,
|
|
266
271
|
aiFeature,
|
|
272
|
+
isWellnessBreakEnabled: isWellnessConfigured && hasAIAssistantLicense,
|
|
267
273
|
};
|
|
268
274
|
|
|
269
275
|
return finalData;
|