Skip to content

feat[contact-center]: Mid call and Post Call summaries - #5171

Open
rsarika wants to merge 61 commits into
webex:nextfrom
rsarika:cc-summaries
Open

feat[contact-center]: Mid call and Post Call summaries #5171
rsarika wants to merge 61 commits into
webex:nextfrom
rsarika:cc-summaries

Conversation

@rsarika

@rsarika rsarika commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

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 own
timeout, 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 matching
inbound frame:

Method Purpose
requestPostCallSummary() Resolves with PostCallSummaryEventPayload
sendPostCallSummaryResponse(payload) Sends the agent's edits, counters, feedback and wrapup code
requestMidCallSummary(actionType) 'CONSULT' | 'TRANSFER'; resolves with MidCallSummaryEventPayload
sendMidCallSummaryResponse(payload, actionType) Sends the agent's edits, counters and feedback

Two new task events, so consumers do not maintain their own interactionId → state maps:

  • task:midCallSummaryForReceivingAgent — the summary that travelled with a transfer, replayed on
    the receiving agent's task even when the frame arrived before that task existed.
  • task:featureEnablement — per-interaction midCallEnabled / postCallEnabled flags used to gate
    the summary UI. Fires at registration time if the frame arrived first.

AISummaryCoordinator (new) owns the request/response correlation the consumer would otherwise
write: pending-request maps keyed by conversation, a 30s timeout per request
(AI_SUMMARY_REQUEST_TIMEOUT_MS), buffering of early receiver summaries and orphan feature-flag
frames with matching retention windows, cancellation on task teardown, and rejection of a duplicate
in-flight request with AI_SUMMARY_REQUEST_ALREADY_PENDING.

TransportApiAiAssistant.sendSummaryGetEvent() / sendSummaryResponseEvent(), with routing
for the new inbound frames through CC_AI_SUMMARY_EVENTS.

Failure modes are typed, not thrown stringsAI_SUMMARY_ERROR_CODES covers
POST_CALL_SUMMARY_DISABLED, MID_CALL_SUMMARY_DISABLED, POST_CALL_SUMMARY_TIMEOUT,
MID_CALL_SUMMARY_TIMEOUT, AI_ASSISTANT_BASE_URL_NOT_AVAILABLE and
AI_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 — plus
CC_AI_SUMMARY_EVENTS, AI_SUMMARY_ERROR_CODES and AIAssistantEventName.

Sample app (docs/samples/contact-center/) exercises the whole surface end to end: summary
panels for consult, transfer, incoming-transfer and wrapup; read-only and edit modes rendered from
the payload's sections map with a summaryText fallback; a delta payload so only edited sections
are sent; thumbs up/down feedback and view/edit/copy counters; suggested wrapup codes applied to the
dropdown; and sessionStorage persistence 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 tracking
and ignored, and the ai-docs under contact-center/src are updated to match the new flows.

https://app.vidcast.io/share/c826de62-f149-49ac-83f1-8cfa32a9ffc3

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

Automatedyarn jest in packages/@webex/contact-center: 34 suites, 1088 tests, all
passing.
Unit tests added or extended for every new unit:
AISummaryCoordinator, Task, TaskManager, TaskUtils, ApiAiAssistant, cc, package index,
config, core/Utils and task/voice/Voice. Coverage includes:

  • post-call request → resolve → sendPostCallSummaryResponse with edits, counters and wrapup code
  • mid-call request for both CONSULT and TRANSFER, and the response for each
  • summary frame arriving before the task is registered (receiver buffer and orphan
    feature-enablement retention), and the buffer expiring
  • request timeout at 30s for both summary types, and cancellation on task teardown
  • duplicate in-flight request rejected with AI_SUMMARY_REQUEST_ALREADY_PENDING
  • feature-disabled and missing-base-URL rejections
  • inbound frames with no matching task counted as dropped
  • metrics emitted on each success and failure path

Manual, against a live org (walkthrough linked above):

  • consult and transfer from the initiating agent — summary requested, reviewed, edited, sent
  • receiving agent sees the transferred summary on task assignment
  • post-call summary during wrapup — reviewed, edited, feedback given, submitted with the wrapup code
  • summary panels gated correctly when midCallEnabled / postCallEnabled are off
  • agent wraps up before the summary arrives — response still sent once it resolves
  • summary never arrives — NOT_RECEIVED state sent; summary ignored — IGNORED state sent
  • refresh mid-wrapup restores the pending post-call summary from sessionStorage

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

      Claude Code

  • 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.

Ravi Chandra Sekhar Sarika added 30 commits August 7, 2026 11:31
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.
Ravi Chandra Sekhar Sarika and others added 3 commits August 18, 2026 00:35
# 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>

ghost left a comment

Copy link
Copy Markdown

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: 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".

Comment on lines +4020 to +4021
const wrapupInteractionId = currentTask?.data?.interactionId;
const wrapupSummaryFeatures = summaryFeatureMap.get(wrapupInteractionId) || {};

ghost Aug 17, 2026

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 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 👍 / 👎.

Ravi Chandra Sekhar Sarika and others added 2 commits August 18, 2026 01:28
…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>

ghost left a comment

Copy link
Copy Markdown

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: 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".

Comment on lines +441 to +442
/** Emitted when AI summary feature enablement changes for an interaction */
FEATURE_ENABLEMENT = 'cc:featureEnablement',

ghost Aug 18, 2026

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 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 👍 / 👎.

Comment on lines +658 to +659
const consultFeatures = summaryFeatureMap.get(consultInteractionId) || {};
if (!consultFeatures.midCallEnabled) {

ghost Aug 18, 2026

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 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 👍 / 👎.

@rsarika rsarika added the validated If the pull request is validated for automation. label Aug 18, 2026
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>
@github-actions github-actions Bot removed the validated If the pull request is validated for automation. label Aug 18, 2026
@rsarika rsarika added the validated If the pull request is validated for automation. label Aug 18, 2026

ghost left a comment

Copy link
Copy Markdown

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: 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) => {

ghost Aug 18, 2026

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 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>
@github-actions github-actions Bot removed the validated If the pull request is validated for automation. label Aug 18, 2026

ghost left a comment

Copy link
Copy Markdown

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: 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',

ghost Aug 18, 2026

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 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);

ghost Aug 18, 2026

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 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 👍 / 👎.

# Conflicts:
#	docs/samples/contact-center/app.js
#	packages/@webex/contact-center/src/cc.ts
#	packages/@webex/contact-center/src/constants.ts
#	packages/@webex/contact-center/src/services/task/Task.ts
#	packages/@webex/contact-center/src/services/task/TaskFactory.ts
#	packages/@webex/contact-center/src/services/task/TaskManager.ts
#	packages/@webex/contact-center/src/services/task/ai-docs/ARCHITECTURE.md
#	packages/@webex/contact-center/src/services/task/types.ts
#	packages/@webex/contact-center/test/unit/spec/cc.ts
#	packages/@webex/contact-center/test/unit/spec/services/task/voice/Voice.ts

ghost left a comment

Copy link
Copy Markdown

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: d556e9f33f

ℹ️ 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 +471 to +479
this.metricsManager.trackEvent(
metricName,
{
taskId: this.data?.interactionId,
duration_ms: Math.max(0, Date.now() - operationStartedAt),
...fields,
} as never,
['operational']
);

ghost Aug 24, 2026

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 Isolate summary operation metrics from product results

Guard this tracking call so telemetry failures cannot change the result of a completed summary operation. Each request/response method invokes this helper inside its product try block, so if MetricsManager.trackEvent() throws after a summary is received or a response is accepted, control enters the failure path and the public API rejects; response callers may then retry an event the backend already processed. Metrics must remain non-blocking.

AGENTS.md reference: packages/@webex/contact-center/AGENTS.md:L44-L50

Useful? React with 👍 / 👎.

Comment on lines +391 to +398
this.trackFeatureEnablementReceived({
validationOutcome,
postCallEnabled:
featurePayload.postCallEnabled === undefined ? 'absent' : featurePayload.postCallEnabled,
midCallEnabled:
featurePayload.midCallEnabled === undefined ? 'absent' : featurePayload.midCallEnabled,
});
this.aiSummaryCoordinator.setFeatureEnablement(featurePayload, matchingTask !== undefined);

ghost Aug 24, 2026

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 Store feature flags before recording telemetry

Move or isolate this metric so a telemetry exception cannot abort feature processing. In the inspected FEATURE_ENABLEMENT path, this call runs before setFeatureEnablement() and task emission; the outer realtime dispatcher catches the exception and returns, leaving a valid interaction without flags, which makes both summary request APIs reject as disabled. The added unit test that makes this tracker throw confirms this exact dispatch path currently stops here.

AGENTS.md reference: packages/@webex/contact-center/AGENTS.md:L44-L50

Useful? React with 👍 / 👎.

@rsarika rsarika added the validated If the pull request is validated for automation. label Aug 24, 2026
@github-actions github-actions Bot removed the validated If the pull request is validated for automation. label Aug 26, 2026

ghost left a comment

Copy link
Copy Markdown

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: adc46edbbf

ℹ️ 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.aiSummaryCoordinator.clearAISummaryState();
}

private configureTaskAISummary(task: ITask): void {

ghost Aug 26, 2026

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 Configure tasks recovered after owner promotion

Invoke this configuration path for tasks created by handleContactOwnerChanged() as well. When an owner-promotion event reconstructs a missing task, that handler creates and registers it without calling configureTaskAISummary() or retaining/delivering its feature snapshot, unlike every other factory path; consequently requestPostCallSummary() and requestMidCallSummary() on the recovered task always reject with AI_SUMMARY_NOT_INITIALIZED, even when summaries are enabled.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

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: d2e1f69549

ℹ️ 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".

summaryReceived: true,
summary: buildSummaryPayload('consult-summary'),
numberOfTimesViewed: midCallSummary.numberOfTimesViewed,
numberOfTimesEdited: midCallSummary.numberOfTimesEdited,

ghost Aug 26, 2026

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 Record edits before cancellation responses

When an agent edits a generated consult summary and then cancels the dialog, this branch sends the edited output from buildSummaryPayload() but leaves numberOfTimesEdited at zero because it never performs the isSummaryEdited() check used by the initiation path. The transfer-cancellation branch has the same behavior, so both flows report inaccurate interaction counters; detect the edit and increment the counter before constructing either cancellation response.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

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: eb933b62aa

ℹ️ 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 +93 to +94
): boolean =>
knownSectionKeys.every((key) => payload[key] === undefined || typeof payload[key] === 'string');

ghost Aug 26, 2026

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 Validate every extension section value

When an initiator frame contains an extension section such as sections: {futureSection: {invalid: true}}, this helper validates only the known keys and accepts the payload. The request then resolves as PostCallSummaryEventPayload or MidCallSummaryEventPayload, whose section values are declared as strings, so JavaScript consumers that trust the public contract can fail when formatting the unexpected object. Preserve support for future section names, but require every present section value to be a string.

Useful? React with 👍 / 👎.

midCallEnabled: !!payload.midCallEnabled,
postCallEnabled: !!payload.postCallEnabled,
});
saveSummaryState(payload.interactionId);

ghost Aug 26, 2026

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 Scope persisted summary state to the event's task

When multiple tasks coexist, a feature-enablement event for task B calls saveSummaryState(B), but that function serializes the single global postCallSummary, which may still contain task A's generated or edited summary. This stores A's content under B's interaction ID, and a later hydrate or reload of B can restore and display the wrong contact's summary. Persist only B's task-scoped flags here, or maintain summary state per interaction instead of copying the global summary.

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown

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: 02de25bc4a

ℹ️ 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 +151 to +153
export const CC_AI_SUMMARY_EVENTS = {
POST_CALL_SUMMARY: 'POST_CALL_SUMMARY',
MID_CALL_SUMMARY: 'MID_CALL_SUMMARY',

ghost Aug 26, 2026

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 the legacy summary members on CC_TASK_EVENTS

When an existing consumer upgrades while referencing the publicly exported CC_TASK_EVENTS.MID_CALL_SUMMARY or .POST_CALL_SUMMARY, both properties now become undefined at runtime (and disappear from its TypeScript surface) because they were moved exclusively to the new CC_AI_SUMMARY_EVENTS object. Keep aliases on CC_TASK_EVENTS for compatibility or provide an explicit migration plan rather than breaking the existing package API.

AGENTS.md reference: packages/@webex/contact-center/AGENTS.md:L83-L85

Useful? React with 👍 / 👎.

ghost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One additional inline finding from this pass.


// Clean up task creation time tracking
taskCreationTimes.delete(task.data.interactionId);
summaryFeatureMap.delete(task.data.interactionId);

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The termination cleanup removes the in-memory feature flags but leaves this interaction's persisted post-call summary in sessionStorage. If a task ends before wrapupCall() (for example, a non-wrapup termination), the generated summary remains available for the rest of the browser session and the storage map grows indefinitely. Call clearSummaryStorageState(task.data.interactionId) here alongside the other task-end cleanup.

ghost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Defect-focused review findings.

* @enum {string}
* @public
*/
export const CC_AI_SUMMARY_EVENTS = {

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Preserve the legacy CC_TASK_EVENTS.MID_CALL_SUMMARY and .POST_CALL_SUMMARY members (for example as aliases). Moving them exclusively to CC_AI_SUMMARY_EVENTS makes existing SDK consumers receive undefined at runtime after upgrade, which is a breaking public-API change.

return correlation?.interactionId === featurePayload.interactionId;
});

this.trackFeatureEnablementReceived({

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Keep telemetry non-blocking here. If trackFeatureEnablementReceived() throws, this valid frame is never retained or emitted, so the summary APIs later reject as disabled. Store/deliver the flags first, or isolate metrics failures.


this.postCallSummaryResponseContext = {conversationId, interactionId};

this.trackAISummaryOperation(

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Isolate this metric from the product operation. A MetricsManager.trackEvent() failure after a summary has already resolved sends control to the catch block and rejects the public API; response callers may then retry an event the backend already accepted.

}
}

private handleReceivingAgentSummaryEvent(payload: Record<string, unknown>): void {

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Validate the complete receiving-summary payload before casting and emitting it. With only these two checks, {conversationId: 'c', summaryText: {invalid: true}} is delivered as MidCallSummaryReceivingAgentPayload even though consumers are promised a string summary.

this.configureTaskAISummary(task);
this.setupTaskListeners(task);
this.taskCollection[payload.interactionId] = task;
this.retainFeatureEnablementForTask(task);

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] This replays retained feature flags before the lifecycle pipeline publishes the reserved task to consumers. A host therefore cannot yet attach task:featureEnablement, and the only notification is lost. Replay after the task has been surfaced.


// Clean up task creation time tracking
taskCreationTimes.delete(task.data.interactionId);
summaryFeatureMap.delete(task.data.interactionId);

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Also clear sessionStorage here. A task that terminates outside wrapupCall() leaves its generated post-call summary persisted for the rest of the session, which retains stale contact content and grows the storage map.

if (midCallSummary.payload) {
await currentTask.sendMidCallSummaryResponse({
summaryReceived: true,
summary: buildSummaryPayload('consult-summary'),

ghost Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Capture the task that opened this flow instead of using mutable currentTask when the dialog closes. If another task is assigned while a summary is pending, this response can be sent with the second task's interaction/conversation identifiers.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants