@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.
Files changed (107) hide show
  1. package/.sdd/manifest.json +6 -5
  2. package/ai-docs/CONTRACTS.md +8 -0
  3. package/ai-docs/SERVICE_STATE.md +4 -0
  4. package/ai-docs/contact-center-spec.md +26 -3
  5. package/ai-docs/features/contact-owner-change-refactor.md +78 -0
  6. package/dist/cc.js +202 -9
  7. package/dist/cc.js.map +1 -1
  8. package/dist/constants.js +5 -1
  9. package/dist/constants.js.map +1 -1
  10. package/dist/index.js +25 -10
  11. package/dist/index.js.map +1 -1
  12. package/dist/metrics/behavioral-events.js +6 -0
  13. package/dist/metrics/behavioral-events.js.map +1 -1
  14. package/dist/metrics/constants.js +6 -0
  15. package/dist/metrics/constants.js.map +1 -1
  16. package/dist/services/AnswerCallOnWebexService.js +4 -4
  17. package/dist/services/AnswerCallOnWebexService.js.map +1 -1
  18. package/dist/services/ApiAiAssistant.js +116 -1
  19. package/dist/services/ApiAiAssistant.js.map +1 -1
  20. package/dist/services/WebexCrossClientService.js +5 -2
  21. package/dist/services/WebexCrossClientService.js.map +1 -1
  22. package/dist/services/WxAppTelephonyMercurySync.js +6 -3
  23. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -1
  24. package/dist/services/config/Util.js +5 -1
  25. package/dist/services/config/Util.js.map +1 -1
  26. package/dist/services/config/constants.js +10 -1
  27. package/dist/services/config/constants.js.map +1 -1
  28. package/dist/services/config/index.js +51 -0
  29. package/dist/services/config/index.js.map +1 -1
  30. package/dist/services/config/types.js +4 -3
  31. package/dist/services/config/types.js.map +1 -1
  32. package/dist/services/core/Utils.js +5 -1
  33. package/dist/services/core/Utils.js.map +1 -1
  34. package/dist/services/task/TaskFactory.js +1 -0
  35. package/dist/services/task/TaskFactory.js.map +1 -1
  36. package/dist/services/task/TaskManager.js +50 -58
  37. package/dist/services/task/TaskManager.js.map +1 -1
  38. package/dist/services/task/types.js.map +1 -1
  39. package/dist/services/task/voice/Voice.js +63 -44
  40. package/dist/services/task/voice/Voice.js.map +1 -1
  41. package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
  42. package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
  43. package/dist/services/task/voice/wxAppVoiceMethods.js +52 -11
  44. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -1
  45. package/dist/services/wxAppDiagnosticLogging.js +50 -13
  46. package/dist/services/wxAppDiagnosticLogging.js.map +1 -1
  47. package/dist/types/cc.d.ts +21 -1
  48. package/dist/types/constants.d.ts +4 -0
  49. package/dist/types/index.d.ts +11 -1
  50. package/dist/types/metrics/constants.d.ts +6 -0
  51. package/dist/types/services/ApiAiAssistant.d.ts +35 -2
  52. package/dist/types/services/config/constants.d.ts +5 -0
  53. package/dist/types/services/config/types.d.ts +15 -2
  54. package/dist/types/services/core/Utils.d.ts +2 -0
  55. package/dist/types/services/task/types.d.ts +2 -0
  56. package/dist/types/services/task/voice/Voice.d.ts +15 -2
  57. package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
  58. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +19 -6
  59. package/dist/types/services/wxAppDiagnosticLogging.d.ts +12 -0
  60. package/dist/types/types.d.ts +56 -1
  61. package/dist/types.js +39 -2
  62. package/dist/types.js.map +1 -1
  63. package/dist/webex.js +9 -1
  64. package/dist/webex.js.map +1 -1
  65. package/package.json +9 -9
  66. package/src/cc.ts +294 -25
  67. package/src/constants.ts +4 -0
  68. package/src/index.ts +11 -0
  69. package/src/metrics/ai-docs/metrics-spec.md +61 -20
  70. package/src/metrics/behavioral-events.ts +6 -0
  71. package/src/metrics/constants.ts +6 -0
  72. package/src/services/AnswerCallOnWebexService.ts +4 -4
  73. package/src/services/ApiAiAssistant.ts +153 -1
  74. package/src/services/WebexCrossClientService.ts +7 -2
  75. package/src/services/WxAppTelephonyMercurySync.ts +3 -3
  76. package/src/services/ai-docs/services-spec.md +11 -3
  77. package/src/services/config/Util.ts +6 -0
  78. package/src/services/config/ai-docs/config-spec.md +14 -3
  79. package/src/services/config/constants.ts +12 -0
  80. package/src/services/config/index.ts +65 -0
  81. package/src/services/config/types.ts +13 -2
  82. package/src/services/core/Utils.ts +4 -0
  83. package/src/services/task/TaskFactory.ts +1 -0
  84. package/src/services/task/TaskManager.ts +85 -87
  85. package/src/services/task/ai-docs/task-spec.md +7 -3
  86. package/src/services/task/types.ts +16 -0
  87. package/src/services/task/voice/Voice.ts +74 -52
  88. package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
  89. package/src/services/task/voice/wxAppVoiceMethods.ts +83 -24
  90. package/src/services/wxAppDiagnosticLogging.ts +135 -23
  91. package/src/types.ts +65 -0
  92. package/src/webex.js +8 -0
  93. package/test/unit/spec/cc.ts +469 -19
  94. package/test/unit/spec/services/ApiAiAssistant.ts +134 -2
  95. package/test/unit/spec/services/WebexCrossClientService.ts +45 -3
  96. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +1 -1
  97. package/test/unit/spec/services/config/Util.ts +44 -0
  98. package/test/unit/spec/services/config/index.ts +68 -0
  99. package/test/unit/spec/services/task/TaskManager.ts +214 -3
  100. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +33 -0
  101. package/test/unit/spec/services/task/voice/Voice.ts +10 -51
  102. package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
  103. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +121 -5
  104. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +44 -8
  105. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  106. package/umd/contact-center.min.js +2 -2
  107. 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 follow-up review remediation / 2026-07-21 |
15
- | Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); coverage remains Partial |
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 107 defined metric names, 96 have behavioral taxonomy and 11 do not. Events **without** an `eventTaxonomyMap` entry are the eight `AI_ASSISTANT_*` names plus `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, and `WEBSOCKET_EVENT_RECEIVED`.
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 107 names from `src/metrics/constants.ts`; taxonomy presence is checked against `src/metrics/behavioral-events.ts`: 96 mapped and 11 unmapped.
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: `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED`, `AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS`, `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_FAILED`, `AI_ASSISTANT_GET_REAL_TIME_ASSISTANCE_SUCCESS`, `AI_ASSISTANT_SEND_EVENT_FAILED`, `AI_ASSISTANT_SEND_EVENT_SUCCESS`, `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_FAILED`, `AI_ASSISTANT_SEND_REAL_TIME_ASSISTANCE_USER_ACTION_SUCCESS`, `WEBSOCKET_DEREGISTER_FAIL`, `WEBSOCKET_DEREGISTER_SUCCESS`, `WEBSOCKET_EVENT_RECEIVED`.
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 following events do **not** have behavioral taxonomy mappings in `behavioral-events.ts`:
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; 73 of 82 names are mapped and the six AI Assistant plus three WebSocket names are intentionally catalogued as unmapped.
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: eleven defined names intentionally have no behavioral taxonomy.
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 eleven unmapped names. |
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]: {
@@ -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.info(`AnswerCallOnWebexService.${logMethod} request`, {
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.info(`AnswerCallOnWebexService.${logMethod} success`, {
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.info('AnswerCallOnWebexService.getCallDetails request', {
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
- {enableWxBetterTogether: enable},
247
+ {
248
+ enableWxBetterTogether: enable,
249
+ usersubPublished: enable,
250
+ },
247
251
  ['operational', 'behavioral']
248
252
  );
249
253
  }
250
254
 
251
- LoggerProxy.info(`Cross-client answer-calls-on-wxcc set to ${enable}`, {
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.info('Subscribed to wxApp telephony Mercury mute sync', {
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 / developer-approved follow-up review remediation / 2026-07-21 |
15
- | Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); coverage remains Partial |
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;