Skip to content

fix(contact-center): consult flow fixes#4861

Open
Kesari3008 wants to merge 12 commits intowebex:task-refactorfrom
Kesari3008:consult-flow-fixes
Open

fix(contact-center): consult flow fixes#4861
Kesari3008 wants to merge 12 commits intowebex:task-refactorfrom
Kesari3008:consult-flow-fixes

Conversation

@Kesari3008
Copy link
Copy Markdown
Contributor

@Kesari3008 Kesari3008 commented Apr 14, 2026

COMPLETES # Consult flow fixes in task-refactor branch

This pull request addresses

Updating UIControls object structure and fixing small buttons visibility and enablement issues for consult flows.

by making the following changes

Updating UIControls object structure and fixing small buttons visibility and enablement issues for consult flows.

Issue Fixed:

  1. Buttons not appearing for consult leg and main interaction.
    Root Cause: We maintained UI controls object per task and when we have 2 logs created: main interaction and consult leg, there was no way to set different controls for each of these legs.
    Fix:

  2. Switch call didn't work on the first click, only from the second click, it actually worked.
    Root Cause: This was happening due to reverse logic in switch call method. When switch is pressed on consult leg, we are supposed to trigger unHold the main interaction and consult leg is placed on hold automatically by backend. And when we press it on the main interaction, hold should be triggered to reverse it. We were doing the revrse of this by using consultMediaResourceID which wasn't working.
    Fix: Updated the behavior to unhold and hold on main interaction media resource ID. This is in parity with Agent Desktop behavior as well,

  3. Consult transfer kept failing with 400 Bad request due to destinationType being sent in payload as 'Agent' instead of 'agent'.
    Fix: Updated the payload to make it lowercase in the API irrespective of what's being passed from client

  4. Post switch call end consult action was causing hold/resume in wrong sequence or was using media resource ID.

  5. Some more small fixes for displaying the controls for each leg and updating it correctly after every action.

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

  1. Consult using buddy agent, tested consult transfer, merge to conference and end consult.
  2. Consult using buddy agent, switch call to main interaction and tested transfer, conference and end consult.
  3. Consult using buddy agent, switch call to main interaction, switch back to consult and tested consult transfer, merge and end consult.
  4. Post the end consult for all scenarios, tested hold/resume and call end.
  5. Post merge conference, tested exit conference from both ends.
  6. Tested consult decline and RONA.
Screenshot 2026-04-15 at 6 19 10 PM Screenshot 2026-04-15 at 6 25 10 PM

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was not used (or, no additional notation is required)
  • Code was generated entirely by GAI
  • GAI was used to create a draft that was subsequently customized or modified
  • Coder created a draft manually that was non-substantively modified by GAI (e.g., refactoring was performed by GAI on manually written code)
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Github Copilot
    • Other - Please Specify
  • This PR is related to
    • Feature
    • Defect fix
    • Tech Debt
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

Make sure to have followed the contributing guidelines before submitting.

…tate for customer disconnect and consult failures with guard protection (webex#4841)"

This reverts commit 3f6991c.
@Kesari3008 Kesari3008 requested a review from a team as a code owner April 14, 2026 09:43
@Kesari3008 Kesari3008 added the validated If the pull request is validated for automation. label Apr 14, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e1d3b82e3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 166 to 168
[TaskEvent.TASK_WRAPUP]: {
target: TaskState.TERMINATED,
actions: ['updateTaskData', 'markEnded', 'emitTaskEnd'],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore CONTACT_ENDED transition in OFFERED state

TaskManager.mapEventToTaskStateMachineEvent still maps CC_EVENTS.CONTACT_ENDED to TaskEvent.CONTACT_ENDED, but the OFFERED state now transitions on TASK_WRAPUP instead. In the pre-answer hangup path, CONTACT_ENDED is now unhandled, so the task can remain stuck in OFFERED (ringing UI persists, cleanup/end emission does not run).

Useful? React with 👍 / 👎.

Comment on lines 170 to 174
// This needs to be handled for all assign failed scenarios (contact, buddy)
// [AgentContactAssignFailed, AgentCtqFailed, AgentBlindTransferFailed,
// [AgentContactAssignFailed, AgentConsultFailed, AgentCtqFailed, AgentBlindTransferFailed,
// AgentVTeamTransferFailed, AgentConsultTransferFailed]
[TaskEvent.ASSIGN_FAILED]: {
target: TaskState.TERMINATED,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle CONSULT_FAILED while task is OFFERED

The mapping layer still emits TaskEvent.CONSULT_FAILED for AGENT_CONSULT_FAILED/AGENT_CTQ_FAILED, but OFFERED no longer has a CONSULT_FAILED transition. When a consulted agent declines or times out before assignment, that failure event is ignored and the incoming consult can remain stuck in OFFERED instead of terminating/clearing consult state.

Useful? React with 👍 / 👎.


import './index';

import config from './webex-config';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Re-export index symbols from webex entrypoint

Removing export * from './index' from the package entrypoint drops runtime named exports (for example constants/classes exported in src/index.ts) even though the package types still point to dist/types/index.d.ts. Existing consumers importing named values from @webex/contact-center will compile against the declarations but fail at runtime because those exports are no longer present.

Useful? React with 👍 / 👎.

@@ -1,4 +1,5 @@
/* eslint-disable import/no-cycle */
import EventEmitter from 'events';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep ITask free of Node EventEmitter dependency

Changing ITask to extend EventEmitter introduces a public type dependency on 'events' (Node typings) and removes the previous lightweight emitter contract. This is a breaking type-surface change for browser-focused TypeScript consumers that do not include Node ambient types, causing declaration resolution errors unrelated to consult-flow behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c36daee0b0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1104 to 1107
const response = await this.contact.unHold({
interactionId: this.data.interactionId,
data: {mediaResourceId: consultMediaResourceId},
data: {mediaResourceId: this.data.mediaResourceId},
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Toggle both media legs when switching consult calls

In switchCall(), the consult→main path now only calls unHold on the main media and returns, while the main→consult path only calls hold on the main media; neither branch updates the consult media leg anymore. During active consult flows this can leave media hold states out of sync with consultCallHeld (e.g., both legs held or both unheld depending on backend behavior), so the SDK reports a switched leg while call control state is not actually switched.

Useful? React with 👍 / 👎.

Comment on lines +54 to 58
return {
main,
consult,
activeLeg,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve legacy flat uiControls fields for runtime consumers

This constructor now emits TaskUIControls as {main, consult, activeLeg} only, removing the previously shipped top-level fields like accept, hold, and transfer from the runtime payload. Existing JavaScript consumers listening to TASK_UI_CONTROLS_UPDATED and reading task.uiControls.accept will now get undefined and break at runtime, so this change introduces a backward-incompatible API regression unless legacy aliases are preserved.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58b9fffc2b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +433 to +437
const hasConsultLeg = Boolean(
consultOwnedBySelf &&
!taskData?.isConsulted &&
!interaction?.isTerminated &&
(consultInProgress || isConsultingState || context.consultCallHeld || hasConsultMedia)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ignore stale consult metadata when computing hasConsultLeg

getVoiceLegState() treats hasConsultMedia alone as sufficient to keep a consult leg alive. If backend/task snapshots retain stale consultMediaResourceId or consult media entries after a consult has ended (a case this commit already acknowledges in other logic), this condition makes hasConsultLeg stay true, which then forces activeLeg to 'consult' and computes controls from consult states even though the call is no longer in consult flow. In practice, post-consult tasks can remain stuck showing consult controls and block normal connected-call actions until a later payload clears those stale fields.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 767f6b763f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +451 to +454
// Initiator already switched back to the main/customer leg
guard: ({context}) =>
context.consultInitiator === true && context.consultCallHeld === true,
target: TaskState.CONNECTED,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate CONSULT_END transition on real hold state

This new branch promotes CONSULT_END to CONNECTED whenever consultCallHeld is true, but that flag is toggled optimistically by SWITCH_TO_MAIN_CALL before the unHold API succeeds. If AGENT_CONSULT_ENDED arrives before the unhold confirmation (or the unhold fails), the machine still moves to CONNECTED even though the primary leg can remain held, and the subsequent revert switch event is no longer handled outside CONSULTING. This leaves task state/UI out of sync with call media state; use backend hold data (like guards.isPrimaryMediaOnHold) when choosing between CONNECTED and HELD here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 35b6e57895

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +247 to 251
if (isConsulting) {
if (!consultInitiator) return DISABLED;
if (consultLegOnHold) return VISIBLE_DISABLED;

return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Hide transfer while consulting from conference

The new isConsulting transfer branch bypasses the later inConference guard, so a consult started from an active conference (conferenceFromBackend/conference context) now shows the regular transfer control as available. In that scenario Voice.transfer() does not use the conference-transfer flow, so users can hit the wrong transfer path (often resulting in failed or incorrect transfer behavior) instead of using transferConference. Please keep transfer hidden/disabled for consult-from-conference states.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8939289465

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +195 to +198
taskData: {
...taskData,
destAgentId: consultDestinationAgentId,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve existing taskData when setting consult destination

setConsultDestination now writes taskData using only the event payload, but TaskEvent.CONSULT events from Voice.consult() do not include taskData. In that path this assignment replaces context.taskData with a partial object ({destAgentId: ...}), dropping interaction/media fields that UI and guards depend on. If consult initiation then fails (CONSULT_FAILED is emitted without taskData), the machine returns to CONNECTED/HELD with truncated context, so controls are computed from incomplete data (for example consult can remain disabled) until a later websocket refresh. Avoid overwriting taskData when the event has no full snapshot, or merge into context.taskData instead.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

validated If the pull request is validated for automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants