///
import EventEmitter from 'events';
import { CallId } from '@webex/calling/dist/types/common/types';
import routingContact from './contact';
import { ITask, TaskResponse, TaskData, TaskId, WrapupPayLoad, ResumeRecordingPayload, ConsultPayload, ConsultEndPayload, TransferPayLoad, ConsultTransferPayLoad } from './types';
import WebCallingService from '../WebCallingService';
import AutoWrapup from './AutoWrapup';
import { WrapupData } from '../config/types';
/**
* Task class represents a contact center task/interaction that can be managed by an agent.
* This class provides all the necessary methods to manage tasks in a contact center environment,
* handling various call control operations and task lifecycle management.
*
* - Task Lifecycle Management:
* - {@link accept} - Accept incoming task
* - {@link decline} - Decline incoming task
* - {@link end} - End active task
* - Media Controls:
* - {@link toggleMute} - Mute/unmute microphone for voice tasks
* - {@link hold} - Place task on hold
* - {@link resume} - Resume held task
* - Recording Controls:
* - {@link pauseRecording} - Pause task recording
* - {@link resumeRecording} - Resume paused recording
* - Task Transfer & Consultation:
* - {@link consult} - Initiate consultation with another agent/queue
* - {@link endConsult} - End ongoing consultation
* - {@link transfer} - Transfer task to another agent/queue
* - {@link consultTransfer} - Transfer after consultation
* - Task Completion:
* - {@link wrapup} - Complete task wrap-up
*
* Key events emitted by Task instances (see {@link TASK_EVENTS} for details):
*
* - Task Lifecycle:
* - task:incoming — New task is being offered
* - task:assigned — Task assigned to agent
* - task:unassigned — Task unassigned from agent
* - task:end — Task has ended
* - task:wrapup — Task entered wrap-up state
* - task:wrappedup — Task wrap-up completed
* - task:rejected — Task was rejected/unanswered
* - task:hydrate — Task data populated
*
* - Media & Controls:
* - task:media — Voice call media track received
* - task:hold — Task placed on hold
* - task:unhold — Task resumed from hold
*
* - Consultation & Transfer:
* - task:consultCreated — Consultation initiated
* - task:consulting — Consultation in progress
* - task:consultAccepted — Consultation accepted
* - task:consultEnd — Consultation ended
* - task:consultQueueCancelled — Queue consultation cancelled
* - task:consultQueueFailed — Queue consultation failed
* - task:offerConsult — Consultation offered
* - task:offerContact — New contact offered
*
* - Recording:
* - task:recordingPaused — Recording paused
* - task:recordingPauseFailed — Recording pause failed
* - task:recordingResumed — Recording resumed
* - task:recordingResumeFailed — Recording resume failed
*
* @implements {ITask}
* @example
* ```typescript
* // 1. Initialize task
* const task = new Task(contact, webCallingService, taskData);
*
* // 2. Set up event listeners
* task.on('task:media', (track) => {
* // Handle voice call media
* const audioElement = document.getElementById('remote-audio');
* audioElement.srcObject = new MediaStream([track]);
* });
*
* task.on('task:hold', () => {
* console.log('Task is on hold');
* // Update UI to show hold state
* });
*
* task.on('task:end', () => {
* console.log('Task ended');
* if (task.data.wrapUpRequired) {
* // Show wrap-up form
* }
* });
*
* // 3. Example task operations
* await task.accept(); // Accept incoming task
* await task.hold(); // Place on hold
* await task.resume(); // Resume from hold
* await task.end(); // End task
*
* // 4. Handle wrap-up if required
* await task.wrapup({
* auxCodeId: 'RESOLVED',
* wrapUpReason: 'Customer issue resolved'
* });
* ```
*/
export default class Task extends EventEmitter implements ITask {
private contact;
private localAudioStream;
private webCallingService;
data: TaskData;
private metricsManager;
webCallMap: Record;
private wrapupData;
autoWrapup?: AutoWrapup;
private agentId;
/**
* Creates a new Task instance which provides the following features:
* @param contact - The routing contact service instance
* @param webCallingService - The web calling service instance
* @param data - Initial task data
* @param wrapupData - Wrap-up configuration data
*/
constructor(contact: ReturnType, webCallingService: WebCallingService, data: TaskData, wrapupData: WrapupData, agentId: string);
/**
* Sets up the automatic wrap-up timer if wrap-up is required
* @private
*/
private setupAutoWrapupTimer;
/**
* Cancels the automatic wrap-up timer if it's running
* @public - Public so it can be called externally when needed
* Note: This is supported only in single session mode. Not supported in multi-session mode.
*/
cancelAutoWrapupTimer(): void;
/**
* @ignore
* @private
*/
private handleRemoteMedia;
/**
* @ignore
* @private
*/
private registerWebCallListeners;
/**
* @ignore
*/
unregisterWebCallListeners(): void;
/**
* Updates the task data with new information
* @param updatedData - New task data to merge with existing data
* @param shouldOverwrite - If true, completely replace data instead of merging
* @returns The updated task instance
* @example
* ```typescript
* task.updateTaskData(newData);
* task.updateTaskData(newData, true); // completely replace data
* ```
*/
updateTaskData: (updatedData: TaskData, shouldOverwrite?: boolean) => this;
/**
* Recursively merges old data with new data
* @private
*/
private reconcileData;
/**
* Agent accepts the incoming task.
* After accepting, the task will emit task:assigned event and for voice calls,
* a task:media event with the audio stream.
*
* @returns Promise
* @throws Error if accepting task fails or media requirements not met
* @example
* ```typescript
* // Set up event handlers before accepting
* task.on(TASK_EVENTS.TASK_ASSIGNED, () => {
* console.log('Task assigned, ID:', task.data.interactionId);
* // Update UI to show active task
* });
*
* // For voice calls, handle media
* task.on(TASK_EVENTS.TASK_MEDIA, (track) => {
* const audioElement = document.getElementById('remote-audio');
* audioElement.srcObject = new MediaStream([track]);
* });
*
* // Accept the task
* try {
* await task.accept();
* console.log('Successfully accepted task');
* } catch (error) {
* console.error('Failed to accept task:', error);
* // Handle error (e.g., show error message to agent)
* }
* ```
*/
accept(): Promise;
/**
* Agent can mute/unmute their microphone during a WebRTC task.
* This method toggles between muted and unmuted states for the local audio stream.
*
* @returns Promise - Resolves when mute/unmute operation completes
* @throws Error if toggling mute state fails or audio stream is not available
* @example
* ```typescript
* // Toggle mute state
* task.toggleMute()
* .then(() => console.log('Mute state toggled successfully'))
* .catch(error => console.error('Failed to toggle mute:', error));
* ```
*/
toggleMute(): Promise;
/**
* Declines the incoming task. This will reject the task and notify the routing system.
* For voice calls, this is equivalent to declining the incoming call.
*
* @returns Promise
* @throws Error if the decline operation fails
* @example
* ```typescript
* // Decline an incoming task
* task.decline()
* .then(() => console.log('Task declined successfully'))
* .catch(error => console.error('Failed to decline task:', error));
* ```
*/
decline(): Promise;
/**
* Puts the current task/interaction on hold.
* Emits task:hold event when successful. For voice tasks, this mutes the audio.
*
* @param mediaResourceId - Optional media resource ID to use for the hold operation. If not provided, uses the task's current mediaResourceId
* @returns Promise
* @throws Error if hold operation fails
* @example
* ```typescript
* // Set up hold event handler
* task.on(TASK_EVENTS.TASK_HOLD, () => {
* console.log('Task is now on hold');
* // Update UI to show hold state (e.g., enable resume button, show hold indicator)
* document.getElementById('resume-btn').disabled = false;
* document.getElementById('hold-indicator').style.display = 'block';
* });
*
* // Place task on hold
* try {
* await task.hold();
* console.log('Successfully placed task on hold');
* } catch (error) {
* console.error('Failed to place task on hold:', error);
* // Handle error (e.g., show error message, reset UI state)
* }
*
* // Place task on hold with custom mediaResourceId
* try {
* await task.hold('custom-media-resource-id');
* console.log('Successfully placed task on hold with custom mediaResourceId');
* } catch (error) {
* console.error('Failed to place task on hold:', error);
* }
* ```
*/
hold(mediaResourceId?: string): Promise;
/**
* Resumes the task/interaction that was previously put on hold.
* Emits task:resume event when successful. For voice tasks, this restores the audio.
*
* @param mediaResourceId - Optional media resource ID to use for the resume operation. If not provided, uses the task's current mediaResourceId from interaction media
* @returns Promise
* @throws Error if resume operation fails
* @example
* ```typescript
* // Set up resume event handler
* task.on(TASK_EVENTS.TASK_RESUME, () => {
* console.log('Task resumed from hold');
* // Update UI to show active state
* document.getElementById('hold-btn').disabled = false;
* document.getElementById('hold-indicator').style.display = 'none';
* });
*
* // Resume task from hold
* try {
* await task.resume();
* console.log('Successfully resumed task from hold');
* } catch (error) {
* console.error('Failed to resume task:', error);
* // Handle error (e.g., show error message)
* }
*
* // Resume task from hold with custom mediaResourceId
* try {
* await task.resume('custom-media-resource-id');
* console.log('Successfully resumed task from hold with custom mediaResourceId');
* } catch (error) {
* console.error('Failed to resume task:', error);
* }
* ```
*/
resume(mediaResourceId?: string): Promise;
/**
* Ends the task/interaction with the customer.
* Emits task:end event when successful. If task requires wrap-up,
* this will be indicated in the task:end event data.
*
* @returns Promise
* @throws Error if ending task fails
* @example
* ```typescript
* // Set up task end event handler
* task.on(TASK_EVENTS.TASK_END, (data) => {
* console.log('Task ended:', task.data.interactionId);
*
* if (data.wrapUpRequired) {
* // Show wrap-up form
* showWrapupForm();
* } else {
* // Clean up and prepare for next task
* cleanupTask();
* }
* });
*
* // End the task
* try {
* await task.end();
* console.log('Task end request successful');
* } catch (error) {
* console.error('Failed to end task:', error);
* // Handle error (e.g., show error message, retry option)
* }
*
* function showWrapupForm() {
* // Show wrap-up UI with required codes
* document.getElementById('wrapup-form').style.display = 'block';
* }
*
* function cleanupTask() {
* // Reset UI state
* document.getElementById('active-task').style.display = 'none';
* document.getElementById('controls').style.display = 'none';
* }
* ```
*/
end(): Promise;
/**
* Wraps up the task/interaction with the customer.
* This is called after task:end event if wrapUpRequired is true.
* Emits task:wrappedup event when successful.
*
* @param wrapupPayload - WrapupPayLoad containing:
* - auxCodeId: Required ID for the wrap-up code
* - wrapUpReason: Required description of wrap-up reason
* @returns Promise
* @throws Error if task data is unavailable, auxCodeId is missing, or wrapUpReason is missing
* @example
* ```typescript
* // Set up wrap-up events
* task.on(TASK_EVENTS.TASK_WRAPUP, () => {
* console.log('Task ready for wrap-up');
* // Show wrap-up form
* document.getElementById('wrapup-form').style.display = 'block';
* });
*
* task.on(TASK_EVENTS.TASK_WRAPPEDUP, () => {
* console.log('Task wrap-up completed');
* // Clean up UI
* document.getElementById('wrapup-form').style.display = 'none';
* });
*
* // Submit wrap-up
* try {
* const wrapupPayload = {
* auxCodeId: selectedCode, // e.g., 'ISSUE_RESOLVED'
* wrapUpReason: 'Customer issue resolved successfully'
* };
* await task.wrapup(wrapupPayload);
* console.log('Successfully submitted wrap-up');
* } catch (error) {
* console.error('Failed to submit wrap-up:', error);
* // Handle validation errors
* if (error.message.includes('required')) {
* // Show validation error to agent
* }
* }
* ```
*/
wrapup(wrapupPayload: WrapupPayLoad): Promise;
/**
* Pauses the recording for the current voice task.
* Emits task:recordingPaused event when successful.
*
* @returns Promise
* @throws Error if pause recording fails
* @example
* ```typescript
* // Set up recording events
* task.on(TASK_EVENTS.TASK_RECORDING_PAUSED, () => {
* console.log('Recording paused');
* // Update UI to show recording paused state
* document.getElementById('recording-status').textContent = 'Recording Paused';
* document.getElementById('pause-recording-btn').style.display = 'none';
* document.getElementById('resume-recording-btn').style.display = 'block';
* });
*
* task.on(TASK_EVENTS.TASK_RECORDING_PAUSE_FAILED, (error) => {
* console.error('Failed to pause recording:', error);
* // Show error to agent
* });
*
* // Pause recording
* try {
* await task.pauseRecording();
* console.log('Pause recording request sent');
* } catch (error) {
* console.error('Error sending pause recording request:', error);
* // Handle error
* }
* ```
*/
pauseRecording(): Promise;
/**
* Resumes the recording for the voice task that was previously paused.
* Emits task:recordingResumed event when successful.
*
* @param resumeRecordingPayload - Configuration for resuming recording:
* - autoResumed: Indicates if resume was automatic (defaults to false)
* @returns Promise
* @throws Error if resume recording fails
* @example
* ```typescript
* // Set up recording resume events
* task.on(TASK_EVENTS.TASK_RECORDING_RESUMED, () => {
* console.log('Recording resumed');
* // Update UI to show active recording state
* document.getElementById('recording-status').textContent = 'Recording Active';
* document.getElementById('pause-recording-btn').style.display = 'block';
* document.getElementById('resume-recording-btn').style.display = 'none';
* });
*
* task.on(TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, (error) => {
* console.error('Failed to resume recording:', error);
* // Show error to agent
* });
*
* // Resume recording
* try {
* const resumePayload = {
* autoResumed: false // Set to true if triggered by system
* };
* await task.resumeRecording(resumePayload);
* console.log('Resume recording request sent');
* } catch (error) {
* console.error('Error sending resume recording request:', error);
* // Handle error
* }
* ```
*/
resumeRecording(resumeRecordingPayload: ResumeRecordingPayload): Promise;
/**
* Consults another agent or queue on an ongoing task for further assistance.
* During consultation, the original customer is typically placed on hold while
* the agent seeks guidance from another agent or queue.
*
* @param consultPayload - Configuration for the consultation containing:
* - to: ID of the agent or queue to consult with
* - destinationType: Type of destination (AGENT, QUEUE, etc.)
* - holdParticipants: Whether to hold other participants (defaults to true)
* @returns Promise - Resolves with consultation result
* @throws Error if consultation fails or invalid parameters provided
* @example
* ```typescript
* // Consult with another agent
* const consultPayload = {
* to: 'agentId123',
* destinationType: DESTINATION_TYPE.AGENT,
* holdParticipants: true
* };
* task.consult(consultPayload)
* .then(response => console.log('Consultation started successfully'))
* .catch(error => console.error('Failed to start consultation:', error));
*
* // Consult with a queue
* const queueConsultPayload = {
* to: 'salesQueue123',
* destinationType: DESTINATION_TYPE.QUEUE
* };
* task.consult(queueConsultPayload)
* .then(response => console.log('Queue consultation started'))
* .catch(error => console.error('Failed to start queue consultation:', error));
* ```
*/
consult(consultPayload: ConsultPayload): Promise;
/**
* Ends an ongoing consultation session for the task.
* This terminates the consultation while maintaining the original customer connection.
*
* @param consultEndPayload - Configuration for ending the consultation containing:
* - isConsult: Must be true to indicate this is a consultation end
* - taskId: ID of the task being consulted on
* - queueId: (Optional) Queue ID if this was a queue consultation
* - isSecondaryEpDnAgent: (Optional) Indicates if this involves a secondary entry point
* @returns Promise - Resolves when consultation is ended
* @throws Error if ending consultation fails or invalid parameters provided
* @example
* ```typescript
* // End a direct agent consultation
* const consultEndPayload = {
* isConsult: true,
* taskId: 'task123'
* };
* task.endConsult(consultEndPayload)
* .then(response => console.log('Consultation ended successfully'))
* .catch(error => console.error('Failed to end consultation:', error));
*
* // End a queue consultation
* const queueConsultEndPayload = {
* isConsult: true,
* taskId: 'task123',
* queueId: 'queue123'
* };
* task.endConsult(queueConsultEndPayload)
* .then(response => console.log('Queue consultation ended'))
* .catch(error => console.error('Failed to end queue consultation:', error));
* ```
*/
endConsult(consultEndPayload: ConsultEndPayload): Promise;
/**
* Transfer the task to an agent directly or to a queue.
* This is a blind transfer that immediately redirects the task to the specified destination.
*
* @param transferPayload - Transfer configuration containing:
* - to: ID of the agent or queue to transfer to
* - destinationType: Type of destination (AGENT, QUEUE, etc.)
* @returns Promise - Resolves when transfer is completed
* @throws Error if transfer fails or invalid parameters provided
* @example
* ```typescript
* // Transfer to a queue
* const queueTransferPayload = {
* to: 'salesQueue123',
* destinationType: DESTINATION_TYPE.QUEUE
* };
* task.transfer(queueTransferPayload)
* .then(response => console.log('Task transferred to queue successfully'))
* .catch(error => console.error('Failed to transfer to queue:', error));
*
* // Transfer to an agent
* const agentTransferPayload = {
* to: 'agentId123',
* destinationType: DESTINATION_TYPE.AGENT
* };
* task.transfer(agentTransferPayload)
* .then(response => console.log('Task transferred to agent successfully'))
* .catch(error => console.error('Failed to transfer to agent:', error));
* ```
*/
transfer(transferPayload: TransferPayLoad): Promise;
/**
* Transfer the task to the party that was consulted.
* This completes a consultative transfer where the agent first consulted with the target
* before transferring the task. For queue consultations, the transfer is automatically
* directed to the agent who accepted the consultation.
*
* @param consultTransferPayload - Configuration for the consultation transfer containing:
* - to: ID of the agent or queue to transfer to
* - destinationType: Type of destination (AGENT, QUEUE, etc. from CONSULT_TRANSFER_DESTINATION_TYPE)
* @returns Promise - Resolves when consultation transfer is completed
* @throws Error if transfer fails, no agent has accepted a queue consultation, or other validation errors
* @example
* ```typescript
* // Complete consultation transfer to an agent
* const agentConsultTransfer = {
* to: 'agentId123',
* destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT
* };
* task.consultTransfer(agentConsultTransfer)
* .then(response => console.log('Consultation transfer to agent completed'))
* .catch(error => console.error('Failed to complete agent consultation transfer:', error));
*
* // Complete consultation transfer to a queue agent
* const queueConsultTransfer = {
* to: 'queue123',
* destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE
* };
* task.consultTransfer(queueConsultTransfer)
* .then(response => console.log('Consultation transfer to queue agent completed'))
* .catch(error => console.error('Failed to complete queue consultation transfer:', error));
* ```
*/
consultTransfer(consultTransferPayload?: ConsultTransferPayLoad): Promise;
/**
* Starts a consultation conference by merging the consultation call with the main call
*
* Creates a three-way conference between the agent, customer, and consulted party
* Extracts required consultation data from the current task data
* On success, emits a `task:conferenceStarted` event
*
* @returns Promise - Response from the consultation conference API
* @throws Error if the operation fails or if consultation data is invalid
*
* @example
* ```typescript
* try {
* await task.consultConference();
* console.log('Conference started successfully');
* } catch (error) {
* console.error('Failed to start conference:', error);
* }
* ```
*/
consultConference(): Promise;
/**
* Exits the current conference by removing the agent from the conference call
*
* Exits the agent from the conference, leaving the customer and consulted party connected
* On success, emits a `task:conferenceEnded` event
*
* @returns Promise - Response from the conference exit API
* @throws Error if the operation fails or if no active conference exists
*
* @example
* ```typescript
* try {
* await task.exitConference();
* console.log('Successfully exited conference');
* } catch (error) {
* console.error('Failed to exit conference:', error);
* }
* ```
*/
exitConference(): Promise;
/**
* Transfers the current conference to another agent
*
* Moves the entire conference (including all participants) to a new agent,
* while the current agent exits and goes to wrapup
* On success, the current agent receives `task:conferenceEnded` event
*
* @returns Promise - Response from the conference transfer API
* @throws Error if the operation fails or if no active conference exists
*
* @example
* ```typescript
* try {
* await task.transferConference();
* console.log('Conference transferred successfully');
* } catch (error) {
* console.error('Failed to transfer conference:', error);
* }
* ```
*/
transferConference(): Promise;
}