feat[contact-center]: Mid call and Post Call summaries - #5171
Conversation
In git worktrees created for Artificer tasks, node_modules and .husky/_/husky.sh are absent (gitignored, yarn install never runs). The prior fix conditionally sourced _/husky.sh but left the yarn commands running unconditionally, causing Abort trap: 6 via the broken Homebrew node 22.8.0 (ICU/icu4c@78 incompatibility). Change && (conditional source) to || exit 0 (hard gate): if _/husky.sh is absent the hook exits 0 immediately before any yarn call. In the main checkout _/husky.sh is present so hooks run normally.
docs/samples/{calling,meetings,webex}.min.js and their .map files are
build outputs regenerated by `npm test`. While tracked, every build run
inflates `git diff HEAD` by ~88 MB, exceeding the Artificer 16 MiB
implementation-evidence limit. Remove from the index and add missing
entries to .gitignore so future builds leave no git footprint.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e394eb37a
ℹ️ 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".
| */ | ||
| export type TaskResponse = AgentContact | Error | void; | ||
|
|
||
| export type AISummaryActionType = 'CONSULT' | 'TRANSFER'; |
There was a problem hiding this comment.
Document the exported AI-summary types as public
Add JSDoc containing @public to this alias and the other newly exported AI-summary types in this block. They are re-exported from src/index.ts as part of the package API, but currently lack the package's required public-type documentation, leaving the new consumer-facing surface undocumented.
AGENTS.md reference: packages/@webex/contact-center/AGENTS.md:L358-L366
Useful? React with 👍 / 👎.
| const createTask = (interactionId: string) => | ||
| ({ | ||
| data: {interactionId}, | ||
| emit: jest.fn(), |
There was a problem hiding this comment.
Use the mandated Sinon and Chai test stack
Replace the Jest mocks, expectations, and fake timers introduced throughout the new AI-summary suites with Sinon and assert from @webex/test-helper-chai. Keeping these tests on a second mocking/assertion stack violates the repository's explicit unit-test conventions and makes the new suite inconsistent with the required helpers and timer controls.
AGENTS.md reference: AGENTS.md:L43-L48
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8e1158c973
ℹ️ 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".
| this.agentId | ||
| ); | ||
|
|
||
| this.configureTaskAISummary(task); |
There was a problem hiding this comment.
Pass the agent name into reserved tasks
When the normal AGENT_CONTACT_RESERVED path creates a task, the preceding factory call passes only agentId, even though the new constructor parameter carries agentName. Consequently these tasks retain agentName === undefined; every sendMidCallSummaryResponse() then serializes agentName: '', and the transport validation currently allows that invalid response to reach the backend. Pass this.agentName at this creation site, as the other new task-creation paths do.
Useful? React with 👍 / 👎.
| if (midCallSummary.payload) { | ||
| const consultEdited = isSummaryEdited('consult-summary', midCallSummary.payload); | ||
| if (consultEdited) midCallSummary.numberOfTimesEdited += 1; | ||
| const consultSummaryPayload = consultEdited ? buildSummaryPayload('consult-summary') : {}; |
There was a problem hiding this comment.
Preserve unedited summaries in response payloads
When an agent accepts a generated summary without editing it, this branch substitutes {} instead of the rendered/generated summary, while still reporting summaryReceived: true. The same conditional appears in the transfer and post-call branches, so the common unedited path discards the summary content before it is sent to the backend and, for handoffs, leaves the receiving agent without the generated text. Build the response summary from the displayed fields for both edited and unedited cases.
Useful? React with 👍 / 👎.
|
|
||
| task.on('task:midCallSummaryForReceivingAgent', (payload) => { | ||
| console.info('[Receiving agent] mid-call summary buffered, waiting for task:assigned', payload); | ||
| incomingMidCallSummaryPayload = payload; |
There was a problem hiding this comment.
Render receiver summaries that arrive after assignment
If task:midCallSummaryForReceivingAgent fires after the matching task has already emitted task:assigned, this handler only stores the payload in a global variable; the only rendering code is inside the assignment listener, which will not run again. The summary therefore remains hidden and can even be rendered for the next unrelated task that becomes assigned. Render immediately for an already-assigned task or retain the payload per task/conversation rather than in one global slot.
Useful? React with 👍 / 👎.
…ove agentName from response API - app.js: send IGNORED (summaryReceived:false, zero counters) in initiateConsult, initiateTransfer, and wrapupCall when feature is enabled but no summary was triggered - Task.ts: add IGNORED to MID_CALL_SUMMARY_UNAVAILABLE_STATES to accept it on the summaryReceived:false branch - types.ts: extend MidCallUnavailableResponse.state to include IGNORED - Spec docs: document IGNORED branch in ai-summary.md, initiator-flow, postcall-flow; update agentName to reflect SDK-sourced (not caller-provided) contract - ai-docs: update task AGENTS.md and ARCHITECTURE.md to reflect IGNORED on the unavailable branch and the MidCallUnavailableResponse type change - Open issues: remove resolved issues 6 and 8 - Tests: fix DummyTask constructor to supply agentName; remove agentName from createMidCallResponsePayload factory; fix rejection test (summaryReceived:false+IGNORED is now valid); add acceptance test for that branch; add setAgentName to cc mocks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7227a63f12
ℹ️ 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".
| await currentTask.sendMidCallSummaryResponse({ | ||
| summaryReceived: true, | ||
| summary: buildSummaryPayload('consult-summary'), |
There was a problem hiding this comment.
Bind summary responses to the initiating task
Capture the task that opened the consult dialog and send the cancellation response through that task rather than the mutable global currentTask. If another task emits task:assigned while this dialog is awaiting a summary, its handler automatically selects the new task; closing the original dialog then sends the first interaction's summary through the newly selected task, causing the SDK to derive the wrong interaction and conversation identifiers and potentially disclose summary content across interactions.
Useful? React with 👍 / 👎.
…p codes, session persistence
Issue 12: renderSummarySection now renders in read-only mode (labeled text) with an
Edit button; clicking Edit switches to textarea inputs (renderSummaryEditMode).
summaryOriginalPayload map stores original payload per prefix.
Issue 13: buildSummaryPayload now sends only sections that differ from the original
(delta). Unchanged sections are omitted; unedited payloads remain {}.
Issue 14: applySuggestedWrapUpCodes pre-selects the first matching wrapup code in the
dropdown when post-call summary includes a suggestedWrapUpCodes array.
Issue 15: saveSummaryState/restoreSummaryState/clearSummaryStorageState persist post-call
summary payload and feature flags to sessionStorage keyed by interactionId. Restored in
handleTaskHydrate after page refresh; cleared on successful wrapup.
Closes Issues 12, 13, 14, 15 in ai-summary-open-issues.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae51c95c1f
ℹ️ 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".
| console.info('📝 [task:wrapup] Delayed UI update for wrapup controls'); | ||
| updateCallControlUI(currentTask); | ||
| }, 0); | ||
| onWrapupEntry(currentTask); |
There was a problem hiding this comment.
Deduplicate post-call requests on wrap-up
Guard this call so the sample starts only one post-call request per task. Several existing state-machine transitions, such as CONNECTED handling CONTACT_ENDED, execute emitTaskWrapup while entering WRAPPING_UP, whose entry action emits the same event again. Consequently this listener invokes onWrapupEntry() twice, resets the shared summary state, and replaces postCallSummaryPending with a second request that rejects as already pending; if the agent wraps up quickly, wrapupCall() can await that rejected request instead of the real in-flight request and send an incorrect unavailable response.
Useful? React with 👍 / 👎.
| for (const code of suggestedCodes) { | ||
| for (let i = 0; i < wrapupCodesDropdownElm.options.length; i++) { | ||
| const opt = wrapupCodesDropdownElm.options[i]; | ||
| if (opt.value === code || opt.text === code) { |
There was a problem hiding this comment.
Match suggested wrap-up codes by name
Read the suggested code's name when comparing it with dropdown options. The new inbound contract and validator define suggestedWrapUpCodes as objects such as {name: string}, so comparing opt.value or opt.text directly with code always compares a string with an object. As a result, valid backend suggestions never select a wrap-up code in the sample.
Useful? React with 👍 / 👎.
Consumers no longer need to listen to cc:featureEnablement and maintain an interactionId → flags map manually. The SDK now emits the new task:featureEnablement event directly on the task object, covering both cases: frame arrives while task is registered (immediate delivery via handleFeatureEnablementEvent) and frame arrives before task creation (orphan path delivers at registration via deliverFeatureEnablementToTask). - types.ts: add TASK_FEATURE_ENABLEMENT = 'task:featureEnablement' to TASK_EVENTS - TaskManager: emit on matching task in handleFeatureEnablementEvent; extract deliverFeatureEnablementToTask (called only from creation paths) so consumers receive exactly one emission per enablement frame - app.js: remove cc:featureEnablement listener; wire task:featureEnablement inside wireSummaryListeners with sessionStorage persistence - ai-docs/ARCHITECTURE.md: document single-emission guarantee, orphan window boundary, and agentName param in TaskFactory.createTask diagram - ai-summary-open-issues.md: add Issue 17 as wrong implementation — fixed - tests: new TaskManager tests for live-delivery and orphan-path delivery; fix campaign test agentName arg; remove agentName from index.ts fixtures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
For chat, email, and social tasks, the factory accepts agentName but discards it here, and Digital consequently never forwards a name to the base Task. These tasks inherit the new mid-call summary APIs without a media-channel gate, so an enabled digital task can successfully request a summary but sendMidCallSummaryResponse() serializes agentName: '', producing an invalid handoff response. Pass the name through the digital constructor as is done for voice tasks.
ℹ️ 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".
| this.setupTaskListeners(task); | ||
| this.taskCollection[payload.interactionId] = task; | ||
| this.retainFeatureEnablementForTask(task); | ||
| this.deliverFeatureEnablementToTask(task); |
There was a problem hiding this comment.
Emit retained feature flags after publishing the task
When a FEATURE_ENABLEMENT frame arrives before AGENT_CONTACT_RESERVED, this call replays the retained payload before the lifecycle pipeline sends the state-machine event that emits task:incoming. Because consumers do not receive the task object until that later event, they cannot yet attach a task:featureEnablement listener and the only task-level notification is lost. Replay the payload after the task has been surfaced, as is already done for buffered receiving summaries.
AGENTS.md reference: packages/@webex/contact-center/src/services/task/ai-docs/AGENTS.md:L114-L117
Useful? React with 👍 / 👎.
Remove adaptive card parsing paths. renderSummaryText now checks payload.sections first (map of section-name → string, formatted with auto-generated labels), then falls back to payload.summaryText. Removes the now-unused extractInputTextValues and extractTextBlockValues helpers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After removing agentName from the public caller-facing payload types (MidCallReceivedResponse / MidCallUnavailableResponse), the transport union type lost the field. The SDK still injects agentName from agent registration into the mid-call transport payload — add it to the transport-layer type so ApiAiAssistant.ts compiles cleanly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 544af1f176
ℹ️ 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".
| wrapupData?: WrapupData, | ||
| agentId?: string | ||
| agentId?: string, | ||
| agentName?: string |
There was a problem hiding this comment.
Forward agent names into digital tasks
Beyond the reserved-task creation path already noted, the newly added agentName parameter is never forwarded by the factory's Digital branch. Consequently every CHAT, EMAIL, or SOCIAL task has this.agentName === undefined even when its TaskManager caller supplies the profile name, so a mid-call transfer response from those tasks serializes agentName: ''. Pass the name through the Digital constructor and its super(...) call as well.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| private handleReceivingAgentSummaryEvent(payload: Record<string, unknown>): void { | ||
| if (!isNonEmptyString(payload.conversationId)) { |
There was a problem hiding this comment.
Reject malformed receiving-summary fields
When a subsequent-agent frame has a non-empty conversationId, this path accepts and emits the rest of the payload without validating the declared receiver fields. For example, {conversationId: 'c', summaryText: {invalid: true}} is delivered as MidCallSummaryReceivingAgentPayload even though summaryText must be a string; consumers that trust the public type can then fail while rendering it. Apply the same optional-field shape checks used for initiator summaries before selecting a task or buffering the payload.
Useful? React with 👍 / 👎.
# Conflicts: # packages/@webex/contact-center/src/index.ts # packages/@webex/contact-center/src/metrics/ai-docs/AGENTS.md # packages/@webex/contact-center/src/metrics/ai-docs/ARCHITECTURE.md # packages/@webex/contact-center/src/metrics/constants.ts # packages/@webex/contact-center/src/services/ApiAiAssistant.ts # packages/@webex/contact-center/src/services/agent/ai-docs/AGENTS.md # packages/@webex/contact-center/src/services/agent/ai-docs/ARCHITECTURE.md # packages/@webex/contact-center/src/services/task/TaskUtils.ts # packages/@webex/contact-center/src/services/task/ai-docs/ARCHITECTURE.md # packages/@webex/contact-center/test/unit/spec/services/ApiAiAssistant.ts
- metrics/ARCHITECTURE.md: correct AI summary metric names from AI_SUMMARY_POST/MID_CALL_REQUEST_* to AI_SUMMARY_GET_POST/MID_CALL_* to match actual constants.ts definitions - task-spec.md: add TASK_FEATURE_ENABLEMENT and TASK_MID_CALL_SUMMARY_FOR_RECEIVING_AGENT to TASK_EVENTS inventory (count 49 → 51); add agentName? as 7th param in createTask signatures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0c51e5b5d
ℹ️ 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".
| const wrapupInteractionId = currentTask?.data?.interactionId; | ||
| const wrapupSummaryFeatures = summaryFeatureMap.get(wrapupInteractionId) || {}; |
There was a problem hiding this comment.
Retain the wrapping task across the summary wait
When a post-call request is still pending after wrapup() succeeds, task:end can fire during this await and its listener clears currentTask and deletes the task's feature flags. The subsequent branches then either skip the required NOT_RECEIVED/IGNORED response or attempt sendPostCallSummaryResponse() on undefined or a newly selected task. Capture the task and its interaction/feature state before starting wrap-up and use that retained task for the response.
Useful? React with 👍 / 👎.
…eatureEnablement Task-level task:featureEnablement (TASK_EVENTS.TASK_FEATURE_ENABLEMENT) is now the canonical event for SDK consumers. Remove the cc:featureEnablement chain: - cc.ts: delete handleFeatureEnablement and its registration in refreshTaskManagerEventForwarders / runDeregisterCleanup - TaskManager.ts: drop the AGENT_EVENTS.FEATURE_ENABLEMENT emit from handleFeatureEnablementEvent (task-level emit and AISummaryCoordinator state update are preserved) - Remove FeatureEnablementEventPayload import from cc.ts (now unused) - Remove AGENT_EVENTS import from TaskManager.ts (now unused there) - Fix test/unit/spec/cc.ts: remove all assertions that verified the removed forwarding behaviour - Fix test/unit/spec/services/task/TaskManager.ts: strip listener setup and assertions for AGENT_EVENTS.FEATURE_ENABLEMENT from the affected tests; remove the now-unused AGENT_EVENTS import - Fix test/unit/spec/index.ts: add five UserPreference types exported by the upstream merge to EXPECTED_ROOT_EXPORTS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rictEqual Jest stores mock arguments in arrays created in the jest-mock host realm, while test expectations run in the jest sandbox realm. These have different Array.prototype objects, so typeEquality() sees mismatched constructors and returns false — causing toStrictEqual to fail even though the content is identical (hence "serializes to the same string"). Fix: compare the transport payload directly via mock.calls[0][1] rather than wrapping the whole calls array in toStrictEqual. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 12dd0d809a
ℹ️ 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".
| /** Emitted when AI summary feature enablement changes for an interaction */ | ||
| FEATURE_ENABLEMENT = 'cc:featureEnablement', |
There was a problem hiding this comment.
Remove the feature event that is never emitted
When a consumer subscribes to the exported AGENT_EVENTS.FEATURE_ENABLEMENT event on the Contact Center instance, it will never fire: a package-wide source search finds no cc:featureEnablement emission, while TaskManager emits only task:featureEnablement on individual tasks. Either restore the Contact Center forwarder or remove this public enum member and its misleading “Emitted” documentation.
AGENTS.md reference: packages/@webex/contact-center/AGENTS.md:L46-L50
Useful? React with 👍 / 👎.
| const consultFeatures = summaryFeatureMap.get(consultInteractionId) || {}; | ||
| if (!consultFeatures.midCallEnabled) { |
There was a problem hiding this comment.
Check organization flags before enabling summary UI
When an interaction reports midCallEnabled: true but the organization has consultTransferSummariesEnabled disabled or absent, this condition still opens the summary UI and invokes requestMidCallSummary(). The SDK then rejects the request as disabled, after which the sample records it as unavailable and may send a NOT_RECEIVED response even though the administrator disabled the feature; the post-call check in onWrapupEntry() has the same problem. Combine the task flag with the corresponding agentConfig.aiFeature.generatedSummaries flag before starting either flow.
Useful? React with 👍 / 👎.
Render mid-call and post-call summaries from the payload's `sections` map, falling back to `summaryText`, instead of parsing `editAdaptiveCard`: - drop extractEditFields()/isUnresolvedTemplate(); the card walker's helper had been deleted in e4145b4 while two call sites survived, so rendering threw "isUnresolvedTemplate is not defined" and surfaced as "Summary unavailable" on every summary path - section keys are already the contract for edited summaries (`summary: Sections | string`), so the edit delta no longer depends on the card's Input.Text ids matching the section names - pin display order via SUMMARY_SECTION_ORDER; payload key order is not guaranteed and unknown keys render last Also dismiss summaries once wrapup completes. COMPLETED is a final state emitting only task:wrappedup, so task:end -- where dismissAllSummaryUI() was wired -- never fires when wrapUpRequired is true, leaving the panels on screen and the summary in sessionStorage. The task:wrappedup handler now runs the same teardown, which also covers auto-wrapup. It waits on the in-flight wrapupCall() first: that reads the edited fields from the DOM and the flags from summaryFeatureMap only after wrapup() resolves, so tearing down synchronously would drop the agent's edits from the response. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1bf36529bf
ℹ️ 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".
| } | ||
| }); | ||
|
|
||
| task.on('task:postCallSummary', (payload) => { |
There was a problem hiding this comment.
Persist post-call results from the promise handler
This listener never runs because the SDK returns initiating-agent post-call summaries exclusively through requestPostCallSummary() and does not define or emit task:postCallSummary. Consequently the later saveSummaryState() call never stores a summary after it arrives—the feature-enablement handler generally saved only the earlier empty state—so a hydrate or page reload during wrap-up cannot restore the generated summary or its counters. Move the persistence work into the request Promise's success handler.
Useful? React with 👍 / 👎.
…i-docs Delete the scratch documents produced during the artifiser run: requirement.md, ai-summary.md, ai-summary-requirements.md, ai-summary-open-issues.md, the three ai-summary-*-flow.md pages, and design/default/ (design_spec.md and implementation_dag.json). Four in-package ai-docs referenced those files in 11 places, several calling ai-summary.md "the canonical contract", so each link is replaced by the information it carried rather than dropped: - metrics/ai-docs/AGENTS.md now tabulates the six AI summary metrics with owner and success condition, the eight terminal inbound-drop paths, and the explicit privacy allow/deny lists. - metrics/ai-docs/ARCHITECTURE.md names metrics/constants.ts as the source of truth for the literals and spells out the allowed/forbidden field sets. - services/task/ai-docs/AGENTS.md points at types.ts, constants.ts and config/types.ts for public signatures instead of the deleted contract. - services/task/ai-docs/ARCHITECTURE.md gains an "AI Summary Flows" section carrying the post-call and mid-call-initiator sequence diagrams, both IGNORED branch rules, the receiver component diagram and delivery sequence, and the task:midCallSummaryForReceivingAgent usage snippet. Every new relative path resolves to a real file and both new anchors match real headings. Also guard the receiving-agent summary in the sample app: a mid-call summary payload with no non-empty sections and no summaryText is now ignored instead of buffered, and the incoming panel stays hidden rather than showing a placeholder. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 05120c1eb9
ℹ️ 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".
| numberOfTimesEdited: 0, | ||
| numberOfTimesCopied: 0, | ||
| feedback: 'none', | ||
| state: 'IGNORED', |
There was a problem hiding this comment.
Wait for pending summaries before reporting them ignored
When the agent clicks Initiate Consult while requestMidCallSummary() is still pending, midCallSummary.payload and requestFailed are both false, so this branch sends IGNORED and proceeds with the handoff. The request can then resolve after the handoff has started, too late to include the generated summary; the transfer flow has the same race. Track and await the in-flight mid-call request, or disable initiation until it settles, before selecting the response state.
Useful? React with 👍 / 👎.
|
|
||
| task.updateTaskData(updateTaskData); | ||
| this.taskCollection[taskData.interactionId] = task; | ||
| this.retainFeatureEnablementForTask(task); |
There was a problem hiding this comment.
Replay flags after campaign reservation re-keying
When a FEATURE_ENABLEMENT frame for the assigned interaction ID arrives while a campaign-preview task is still stored under its reservation ID, it is retained as an orphan. The assignment path then re-keys the task and this call cancels the orphan timeout, but unlike creation paths it never emits task:featureEnablement; consumers that attached listeners when the reservation task was surfaced therefore never receive the flags and cannot enable summary handling. Ensure the re-key/update path replays the retained payload after the task event is published.
Useful? React with 👍 / 👎.
COMPLETES WXCC-9127
This pull request addresses
WxCC generates AI call summaries — a mid-call summary the agent reviews before a consult or
transfer, and a post-call summary the agent reviews during wrapup — but the JS SDK had no
surface for them. Custom desktops built on this SDK could not request a summary, show it to the
agent, capture the agent's edits and feedback, or hand a summary to the receiving agent on a
transfer.
The delivery model makes this awkward for consumers to do themselves. Summaries are requested over
one channel and delivered asynchronously over Contact Center realtime as a separate inbound frame,
so a consumer would have to correlate request to response by
conversationId, impose its owntimeout, and cope with frames that arrive before the task they belong to is registered (both the
receiving agent's summary and the per-interaction feature flags can land early). Each consumer
re-implementing that correlation is where the bugs live.
by making the following changes
Task API — four methods on
ITask, each returning a promise that settles on the matchinginbound frame:
requestPostCallSummary()PostCallSummaryEventPayloadsendPostCallSummaryResponse(payload)requestMidCallSummary(actionType)'CONSULT' | 'TRANSFER'; resolves withMidCallSummaryEventPayloadsendMidCallSummaryResponse(payload, actionType)Two new task events, so consumers do not maintain their own
interactionId → statemaps:task:midCallSummaryForReceivingAgent— the summary that travelled with a transfer, replayed onthe receiving agent's task even when the frame arrived before that task existed.
task:featureEnablement— per-interactionmidCallEnabled/postCallEnabledflags used to gatethe summary UI. Fires at registration time if the frame arrived first.
AISummaryCoordinator(new) owns the request/response correlation the consumer would otherwisewrite: pending-request maps keyed by conversation, a 30s timeout per request
(
AI_SUMMARY_REQUEST_TIMEOUT_MS), buffering of early receiver summaries and orphan feature-flagframes with matching retention windows, cancellation on task teardown, and rejection of a duplicate
in-flight request with
AI_SUMMARY_REQUEST_ALREADY_PENDING.Transport —
ApiAiAssistant.sendSummaryGetEvent()/sendSummaryResponseEvent(), with routingfor the new inbound frames through
CC_AI_SUMMARY_EVENTS.Failure modes are typed, not thrown strings —
AI_SUMMARY_ERROR_CODEScoversPOST_CALL_SUMMARY_DISABLED,MID_CALL_SUMMARY_DISABLED,POST_CALL_SUMMARY_TIMEOUT,MID_CALL_SUMMARY_TIMEOUT,AI_ASSISTANT_BASE_URL_NOT_AVAILABLEandAI_SUMMARY_REQUEST_ALREADY_PENDING, so a consumer can distinguish "feature off" from "timed out"without string matching.
Behavioral metrics for each get/response success and failure, feature-enablement receipt, and
inbound frames dropped with no matching task.
Public types exported from the package entry point:
PostCallSummaryEventPayload,MidCallSummaryEventPayload,MidCallSummaryReceivingAgentPayload,FeatureEnablementEventPayload,PostCallSummarySections,MidCallSummarySections, the response payload types,SummaryCounters,AISummaryActionType,AISummaryFeedback, and the summary state unions — plusCC_AI_SUMMARY_EVENTS,AI_SUMMARY_ERROR_CODESandAIAssistantEventName.Sample app (
docs/samples/contact-center/) exercises the whole surface end to end: summarypanels for consult, transfer, incoming-transfer and wrapup; read-only and edit modes rendered from
the payload's
sectionsmap with asummaryTextfallback; a delta payload so only edited sectionsare sent; thumbs up/down feedback and view/edit/copy counters; suggested wrapup codes applied to the
dropdown; and
sessionStoragepersistence so a refresh mid-wrapup does not lose the summary.Also in this branch: minified sample bundles (
docs/samples/*.min.js) are removed from git trackingand ignored, and the
ai-docsundercontact-center/srcare updated to match the new flows.https://app.vidcast.io/share/c826de62-f149-49ac-83f1-8cfa32a9ffc3
Change Type
The following scenarios were tested
Automated —
yarn jestinpackages/@webex/contact-center: 34 suites, 1088 tests, allpassing. Unit tests added or extended for every new unit:
AISummaryCoordinator,Task,TaskManager,TaskUtils,ApiAiAssistant,cc, packageindex,config,core/Utilsandtask/voice/Voice. Coverage includes:sendPostCallSummaryResponsewith edits, counters and wrapup codeCONSULTandTRANSFER, and the response for eachfeature-enablement retention), and the buffer expiring
AI_SUMMARY_REQUEST_ALREADY_PENDINGManual, against a live org (walkthrough linked above):
midCallEnabled/postCallEnabledare offNOT_RECEIVEDstate sent; summary ignored —IGNOREDstate sentsessionStorageThe 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
Claude Code
This PR is related to
I certified that
Make sure to have followed the contributing guidelines before submitting.