@webex/internal-plugin-call-ai-summary 0.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +6 -0
- package/README.md +257 -0
- package/ai-docs/AGENTS.md +300 -0
- package/ai-docs/ARCHITECTURE.md +1189 -0
- package/babel.config.js +3 -0
- package/dist/ai-summary.js +418 -0
- package/dist/ai-summary.js.map +1 -0
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.js +25 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/manual-integration-test.js +154 -0
- package/dist/manual-integration-test.js.map +1 -0
- package/dist/manual-pragya-api-test.js +176 -0
- package/dist/manual-pragya-api-test.js.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/jest.config.js +3 -0
- package/package.json +47 -0
- package/src/ai-summary.ts +318 -0
- package/src/config.ts +7 -0
- package/src/constants.ts +19 -0
- package/src/index.ts +13 -0
- package/src/manual-integration-test.js +129 -0
- package/src/manual-pragya-api-test.js +166 -0
- package/src/types.ts +154 -0
package/.eslintrc.js
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# @webex/internal-plugin-call-ai-summary
|
|
2
|
+
|
|
3
|
+
Internal Webex JS SDK plugin for retrieving AI-generated call summaries, notes, action items, and transcripts from completed calls.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin resolves AI summary containers via the **Pragya** service and fetches encrypted summary content from URLs returned by Pragya. All AI-generated content is decrypted using KMS keys provided in the container response.
|
|
8
|
+
|
|
9
|
+
**Discovery flow:**
|
|
10
|
+
|
|
11
|
+
1. **Janus** (call history) returns `extensionPayload.callingContainerIds` per call session
|
|
12
|
+
2. **Pragya** resolves a container ID into metadata including content URLs and encryption key
|
|
13
|
+
3. **Plugin** fetches content from those URLs and decrypts using `@webex/internal-plugin-encryption`
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
This plugin is part of the Webex JS SDK monorepo. It self-registers when imported — no changes to `packages/webex` are needed.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# From the SDK monorepo root
|
|
21
|
+
yarn
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To use in a consuming application:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
// Importing the plugin auto-registers it on webex.internal.aisummary
|
|
28
|
+
import '@webex/internal-plugin-call-ai-summary';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
- An authenticated Webex SDK instance with a registered device
|
|
34
|
+
- `@webex/internal-plugin-encryption` (pulled in automatically as a dependency)
|
|
35
|
+
- A valid Pragya container ID (obtained from Janus call history `extensionPayload.callingContainerIds`)
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
All methods are accessible via `webex.internal.aisummary`.
|
|
40
|
+
|
|
41
|
+
### `getContainer({ containerId })`
|
|
42
|
+
|
|
43
|
+
Resolves a Pragya container by ID. Returns container metadata with summary content URLs and the KMS encryption key.
|
|
44
|
+
|
|
45
|
+
The raw Pragya response nests URLs under `summaryData.data` — this method flattens it so you can access `summaryData.summaryUrl` directly.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const container = await webex.internal.aisummary.getContainer({
|
|
49
|
+
containerId: '34125120-13b5-11f1-9b36-adb685725098',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// container.summaryData.summaryUrl — full summary URL
|
|
53
|
+
// container.summaryData.transcriptUrl — transcript URL
|
|
54
|
+
// container.summaryData.status — "Active" when ready
|
|
55
|
+
// container.encryptionKeyUrl — KMS key for decryption
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Returns:** `Promise<PragyaContainerResponse>`
|
|
59
|
+
|
|
60
|
+
### `getSummary({ containerInfo })`
|
|
61
|
+
|
|
62
|
+
Fetches and decrypts all summary content (note, short note, and action items) in a single request via `summaryUrl?fields=note,shortnote,actionitems`.
|
|
63
|
+
|
|
64
|
+
This is the **recommended** method for retrieving summary content.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const summary = await webex.internal.aisummary.getSummary({
|
|
68
|
+
containerInfo: container,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
console.log(summary.note); // Decrypted full note
|
|
72
|
+
console.log(summary.shortNote); // Decrypted short note
|
|
73
|
+
summary.actionItems.forEach((item) => {
|
|
74
|
+
console.log(item.aiGeneratedContent); // Decrypted action item
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Returns:** `Promise<SummaryContent>` — `{ id, note, shortNote, actionItems, feedbackUrl? }`
|
|
79
|
+
|
|
80
|
+
### `getNotes({ containerInfo })`
|
|
81
|
+
|
|
82
|
+
Fetches and decrypts notes from the standalone `notesUrl` endpoint. Only available if `notesUrl` is present in the Pragya response.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
const notes = await webex.internal.aisummary.getNotes({
|
|
86
|
+
containerInfo: container,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
console.log(notes.content); // Decrypted notes
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Returns:** `Promise<SummaryNotes>` — `{ id, content, feedbackUrl? }`
|
|
93
|
+
|
|
94
|
+
### `getActionItems({ containerInfo })`
|
|
95
|
+
|
|
96
|
+
Fetches and decrypts action items from the standalone `actionItemsUrl` endpoint. Only available if `actionItemsUrl` is present in the Pragya response.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const actionItems = await webex.internal.aisummary.getActionItems({
|
|
100
|
+
containerInfo: container,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
actionItems.snippets.forEach((snippet) => {
|
|
104
|
+
console.log(snippet.aiGeneratedContent); // Decrypted
|
|
105
|
+
console.log(snippet.editedContent); // User-edited version (if any)
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Returns:** `Promise<SummaryActionItems>` — `{ id?, snippets[], feedbackUrl? }`
|
|
110
|
+
|
|
111
|
+
### `getTranscriptUrl({ containerInfo })`
|
|
112
|
+
|
|
113
|
+
Returns the transcript URL string without fetching or decrypting. Use this when you need the URL for downstream processing.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const url = webex.internal.aisummary.getTranscriptUrl({
|
|
117
|
+
containerInfo: container,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Returns:** `string`
|
|
122
|
+
|
|
123
|
+
### `getTranscript({ containerInfo })`
|
|
124
|
+
|
|
125
|
+
Fetches and decrypts the full call transcript.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const transcript = await webex.internal.aisummary.getTranscript({
|
|
129
|
+
containerInfo: container,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
transcript.snippets.forEach((snippet) => {
|
|
133
|
+
console.log(`[${snippet.startTime}] ${snippet.speaker?.speakerName}: ${snippet.content}`);
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Returns:** `Promise<TranscriptContent>` — `{ id, totalCount, snippets[] }`
|
|
138
|
+
|
|
139
|
+
## Full Usage Example
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import '@webex/internal-plugin-call-ai-summary';
|
|
143
|
+
|
|
144
|
+
// 1. Get call history (existing SDK API)
|
|
145
|
+
const callHistory = await callHistoryInstance.getCallHistoryData(10, 50);
|
|
146
|
+
const sessions = callHistory.data.userSessions;
|
|
147
|
+
|
|
148
|
+
// 2. Find a session with AI summary
|
|
149
|
+
const session = sessions.find(
|
|
150
|
+
(s) => s.extensionPayload?.callingContainerIds?.length > 0
|
|
151
|
+
);
|
|
152
|
+
if (!session) return;
|
|
153
|
+
|
|
154
|
+
// 3. Resolve the container
|
|
155
|
+
const containerId = session.extensionPayload.callingContainerIds[0];
|
|
156
|
+
const container = await webex.internal.aisummary.getContainer({ containerId });
|
|
157
|
+
|
|
158
|
+
if (container.summaryData.status !== 'Active') {
|
|
159
|
+
console.log('Summary not ready yet');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 4. Fetch all summary content in one call
|
|
164
|
+
const summary = await webex.internal.aisummary.getSummary({ containerInfo: container });
|
|
165
|
+
console.log('Note:', summary.note);
|
|
166
|
+
console.log('Short Note:', summary.shortNote);
|
|
167
|
+
summary.actionItems.forEach((item, i) => {
|
|
168
|
+
console.log(`Action ${i + 1}: ${item.aiGeneratedContent}`);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// 5. Fetch transcript
|
|
172
|
+
const transcript = await webex.internal.aisummary.getTranscript({ containerInfo: container });
|
|
173
|
+
transcript.snippets.forEach((s) => {
|
|
174
|
+
console.log(`[${s.startTime}] ${s.speaker?.speakerName}: ${s.content}`);
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Manual Testing
|
|
179
|
+
|
|
180
|
+
A manual integration test is provided for verifying against live APIs:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
cd packages/@webex/internal-plugin-call-ai-summary
|
|
184
|
+
|
|
185
|
+
# Provide a fresh token and container ID
|
|
186
|
+
WEBEX_TOKEN='<token>' CONTAINER_ID='<id>' node src/manual-integration-test.js
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
This script registers a device (WDM), resolves the Pragya service via the SDK service catalog, fetches the container, decrypts summary content via KMS, and prints the results.
|
|
190
|
+
|
|
191
|
+
## Error Handling
|
|
192
|
+
|
|
193
|
+
| Error | Cause | Recovery |
|
|
194
|
+
|-------|-------|----------|
|
|
195
|
+
| `containerId is required and must be a non-empty string` | Empty or missing containerId | Validate input before calling |
|
|
196
|
+
| `containerInfo with valid summaryData and encryptionKeyUrl is required` | Missing container info or URL field | Call `getContainer()` first |
|
|
197
|
+
| `Container not found` | 404 from Pragya | Verify containerId from Janus |
|
|
198
|
+
| `Summary content not available or expired` | 404 from content endpoint | Content may have been deleted |
|
|
199
|
+
| `Access denied: User not authorized to view this summary` | 403 | Check user permissions / org AI settings |
|
|
200
|
+
| `Authentication failed: Invalid or expired token` | 401 | Re-authenticate the user |
|
|
201
|
+
|
|
202
|
+
## Encryption
|
|
203
|
+
|
|
204
|
+
All AI-generated content (`aiGeneratedContent` fields) is encrypted with KMS. The plugin decrypts automatically using:
|
|
205
|
+
|
|
206
|
+
- **Key source:** `encryptionKeyUrl` from the Pragya container response, with fallback to `keyUrl` from the content response body
|
|
207
|
+
- **Decryption method:** `webex.internal.encryption.decryptText(keyUrl, ciphertext)`
|
|
208
|
+
|
|
209
|
+
This is the same pattern used by `@webex/internal-plugin-ai-assistant` and `@webex/internal-plugin-task`.
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
cd packages/@webex/internal-plugin-call-ai-summary
|
|
215
|
+
|
|
216
|
+
# Build
|
|
217
|
+
yarn build
|
|
218
|
+
|
|
219
|
+
# Lint
|
|
220
|
+
yarn test:style
|
|
221
|
+
|
|
222
|
+
# Unit tests
|
|
223
|
+
yarn test:unit
|
|
224
|
+
|
|
225
|
+
# All checks
|
|
226
|
+
yarn test
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Package Structure
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
src/
|
|
233
|
+
index.ts # Self-registration via registerInternalPlugin('aisummary', ...)
|
|
234
|
+
ai-summary.ts # Plugin implementation (WebexPlugin.extend)
|
|
235
|
+
config.ts # Plugin config
|
|
236
|
+
constants.ts # Service name, error messages
|
|
237
|
+
types.ts # TypeScript interfaces
|
|
238
|
+
test/
|
|
239
|
+
unit/
|
|
240
|
+
spec/
|
|
241
|
+
ai-summary.ts # Unit tests (26 tests)
|
|
242
|
+
data/
|
|
243
|
+
responses.ts # Mock API response fixtures
|
|
244
|
+
ai-docs/
|
|
245
|
+
ARCHITECTURE.md # Detailed architecture document
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Dependencies
|
|
249
|
+
|
|
250
|
+
| Package | Purpose |
|
|
251
|
+
|---------|---------|
|
|
252
|
+
| `@webex/webex-core` | Plugin infrastructure (`WebexPlugin`, `registerInternalPlugin`) |
|
|
253
|
+
| `@webex/internal-plugin-encryption` | KMS content decryption via `decryptText()` |
|
|
254
|
+
|
|
255
|
+
## Architecture
|
|
256
|
+
|
|
257
|
+
See [ai-docs/ARCHITECTURE.md](ai-docs/ARCHITECTURE.md) for the full architecture document covering data flows, API request/response details, DTOs, security considerations, and testing strategy.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# @webex/internal-plugin-call-ai-summary
|
|
2
|
+
|
|
3
|
+
This is an internal Cisco Webex plugin. As such, it does not strictly adhere to semantic versioning. Use at your own risk. If you're not working on one of our first party clients, please look at our developer api and stick to our public plugins.
|
|
4
|
+
Internal Webex JS SDK plugin for retrieving AI-generated call summaries, notes, action items, and transcript URLs from the Pragya and AI Bridge services.
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This plugin provides methods to:
|
|
9
|
+
|
|
10
|
+
1. Resolve a **Pragya container** by ID (returns metadata, summary URLs, and encryption key)
|
|
11
|
+
2. Fetch and decrypt **AI-generated summaries** (note, short note, action items) in a single call
|
|
12
|
+
3. Fetch and decrypt **AI-generated notes** via a dedicated notes endpoint
|
|
13
|
+
4. Fetch and decrypt **AI-generated action items** via a dedicated action items endpoint
|
|
14
|
+
5. Retrieve the **transcript URL** for a call
|
|
15
|
+
|
|
16
|
+
All AI-generated content is **JWE-encrypted** and decrypted via the KMS (Key Management Service) using `@webex/internal-plugin-encryption`.
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Pragya Service AI Bridge Service
|
|
22
|
+
(container metadata) (summary content)
|
|
23
|
+
| |
|
|
24
|
+
getContainer() getSummary() / getNotes() / getActionItems()
|
|
25
|
+
| |
|
|
26
|
+
v v
|
|
27
|
+
PragyaContainerResponse Encrypted JWE content
|
|
28
|
+
(summaryData, encryptionKeyUrl) |
|
|
29
|
+
v
|
|
30
|
+
KMS Decryption
|
|
31
|
+
(internal-plugin-encryption)
|
|
32
|
+
|
|
|
33
|
+
v
|
|
34
|
+
Decrypted plaintext (HTML)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Note:** The Pragya API returns summary URLs nested under `summaryData.data`. The `getContainer()` method normalizes this automatically, flattening `summaryData.data` into `summaryData` so consumers can access `summaryData.summaryUrl` directly.
|
|
38
|
+
|
|
39
|
+
## Registration
|
|
40
|
+
|
|
41
|
+
The plugin registers itself as `aisummary` on the internal namespace:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import '@webex/internal-plugin-call-ai-summary';
|
|
45
|
+
|
|
46
|
+
// Accessed via:
|
|
47
|
+
webex.internal.aisummary.getContainer({ containerId: '...' });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Source Files
|
|
51
|
+
|
|
52
|
+
| File | Description |
|
|
53
|
+
|------|-------------|
|
|
54
|
+
| `src/index.ts` | Entry point. Registers the plugin via `registerInternalPlugin('aisummary', ...)`. |
|
|
55
|
+
| `src/ai-summary.ts` | Main plugin class extending `WebexPlugin`. Contains all public and private methods. |
|
|
56
|
+
| `src/types.ts` | TypeScript interfaces for request/response DTOs. |
|
|
57
|
+
| `src/constants.ts` | Service name, resource path, and error message constants. |
|
|
58
|
+
| `src/config.ts` | Plugin configuration (currently empty). |
|
|
59
|
+
|
|
60
|
+
## API Reference
|
|
61
|
+
|
|
62
|
+
### `getContainer(options: GetContainerOptions): Promise<PragyaContainerResponse>`
|
|
63
|
+
|
|
64
|
+
Resolves a Pragya container by ID. Returns container metadata including summary URLs and the KMS encryption key URL. Normalizes the response by flattening `summaryData.data` into `summaryData`.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const container = await webex.internal.aisummary.getContainer({
|
|
68
|
+
containerId: '34125120-13b5-11f1-9b36-adb685725098',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// After normalization, URLs are directly on summaryData:
|
|
72
|
+
console.log(container.summaryData.summaryUrl); // https://aibridge-.../summaries/...
|
|
73
|
+
console.log(container.summaryData.transcriptUrl); // https://aibridge-.../transcripts/...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Request**: `GET {pragya-service}/containers/{containerId}`
|
|
77
|
+
|
|
78
|
+
**Response fields**:
|
|
79
|
+
- `summaryData` — Contains summary URLs (`summaryUrl`, `transcriptUrl`, `status`, `summarizeAfterCall`)
|
|
80
|
+
- `encryptionKeyUrl` — KMS key URL for decrypting content (e.g., `kms://kms-aore.wbx2.com/keys/...`)
|
|
81
|
+
- `kmsResourceObjectUrl`, `aclUrl`, `forkSessionId`, `callSessionId`, `ownerUserId`, `orgId`, `start`, `end`
|
|
82
|
+
|
|
83
|
+
### `getSummary(options: GetSummaryContentOptions): Promise<SummaryContent>`
|
|
84
|
+
|
|
85
|
+
Fetches all AI-generated summary content (note, short note, and action items) from a single request to the summary URL, and decrypts each field via KMS. This is the primary method for retrieving summary content.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const summary = await webex.internal.aisummary.getSummary({
|
|
89
|
+
containerInfo: container,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log(summary.note); // Decrypted full note (HTML)
|
|
93
|
+
console.log(summary.shortNote); // Decrypted short note (HTML)
|
|
94
|
+
console.log(summary.actionItems); // Array of decrypted action item snippets
|
|
95
|
+
console.log(summary.feedbackUrl); // Feedback URL from links (if available)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Request**: `GET {summaryUrl}?fields=note,shortnote,actionitems`
|
|
99
|
+
|
|
100
|
+
**Response structure** (from AI Bridge, before decryption):
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"id": "...",
|
|
104
|
+
"keyUrl": "kms://...",
|
|
105
|
+
"note": { "aiGeneratedContent": "<JWE>" },
|
|
106
|
+
"shortnote": { "aiGeneratedContent": "<JWE>" },
|
|
107
|
+
"actionitems": {
|
|
108
|
+
"snippets": [
|
|
109
|
+
{ "id": "...", "aiGeneratedContent": "<JWE>" }
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"links": [
|
|
113
|
+
{ "rel": "feedback", "href": "https://..." }
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Return type** (`SummaryContent`):
|
|
119
|
+
- `id` — Summary identifier
|
|
120
|
+
- `note` — Decrypted full note (HTML string)
|
|
121
|
+
- `shortNote` — Decrypted short note (HTML string)
|
|
122
|
+
- `actionItems` — Array of `ActionItemSnippet` objects
|
|
123
|
+
- `feedbackUrl` — Extracted from `links` array (`rel: "feedback"`), if available
|
|
124
|
+
|
|
125
|
+
### `getNotes(options: GetSummaryContentOptions): Promise<SummaryNotes>`
|
|
126
|
+
|
|
127
|
+
Fetches AI-generated notes from the dedicated notes endpoint and decrypts via KMS. Requires `notesUrl` to be present in the container's `summaryData`.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const notes = await webex.internal.aisummary.getNotes({
|
|
131
|
+
containerInfo: container,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
console.log(notes.content); // Decrypted notes content
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Request**: `GET {notesUrl}`
|
|
138
|
+
|
|
139
|
+
> **Note:** The `notesUrl` may not be present in all API versions. Prefer `getSummary()` which returns notes, short notes, and action items in a single call.
|
|
140
|
+
|
|
141
|
+
### `getActionItems(options: GetSummaryContentOptions): Promise<SummaryActionItems>`
|
|
142
|
+
|
|
143
|
+
Fetches AI-generated action items from the dedicated action items endpoint and decrypts each snippet via KMS. Requires `actionItemsUrl` to be present in the container's `summaryData`.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const actionItems = await webex.internal.aisummary.getActionItems({
|
|
147
|
+
containerInfo: container,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
actionItems.snippets.forEach((item) => {
|
|
151
|
+
console.log(item.aiGeneratedContent); // Decrypted action item
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Request**: `GET {actionItemsUrl}`
|
|
156
|
+
|
|
157
|
+
> **Note:** The `actionItemsUrl` may not be present in all API versions. Prefer `getSummary()` which returns notes, short notes, and action items in a single call.
|
|
158
|
+
|
|
159
|
+
### `getTranscriptUrl(options: GetSummaryContentOptions): string`
|
|
160
|
+
|
|
161
|
+
Returns the transcript URL from the container info. Does not fetch or decrypt content.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const transcriptUrl = webex.internal.aisummary.getTranscriptUrl({
|
|
165
|
+
containerInfo: container,
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Types
|
|
170
|
+
|
|
171
|
+
### Request Types
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
interface GetContainerOptions {
|
|
175
|
+
containerId: string; // Pragya container ID
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface GetSummaryContentOptions {
|
|
179
|
+
containerInfo: PragyaContainerResponse; // Resolved container from getContainer()
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Response Types
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
interface PragyaContainerResponse {
|
|
187
|
+
summaryData: PragyaSummaryData;
|
|
188
|
+
encryptionKeyUrl: string;
|
|
189
|
+
kmsResourceObjectUrl: string;
|
|
190
|
+
aclUrl: string;
|
|
191
|
+
forkSessionId: string;
|
|
192
|
+
callSessionId: string;
|
|
193
|
+
ownerUserId: string;
|
|
194
|
+
orgId: string;
|
|
195
|
+
start: string;
|
|
196
|
+
end: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
interface PragyaSummaryData {
|
|
200
|
+
status: string;
|
|
201
|
+
summaryUrl: string;
|
|
202
|
+
transcriptUrl: string;
|
|
203
|
+
summarizeAfterCall: boolean;
|
|
204
|
+
notesUrl?: string; // May not be present in all API versions
|
|
205
|
+
actionItemsUrl?: string; // May not be present in all API versions
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
interface SummaryContent {
|
|
209
|
+
id: string;
|
|
210
|
+
note: string; // Decrypted full note (HTML)
|
|
211
|
+
shortNote: string; // Decrypted short note (HTML)
|
|
212
|
+
actionItems: ActionItemSnippet[];
|
|
213
|
+
feedbackUrl?: string; // From links array (rel="feedback")
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
interface SummaryNotes {
|
|
217
|
+
id: string;
|
|
218
|
+
content: string; // Decrypted notes content
|
|
219
|
+
feedbackUrl?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
interface SummaryActionItems {
|
|
223
|
+
id: string;
|
|
224
|
+
snippets: ActionItemSnippet[];
|
|
225
|
+
feedbackUrl?: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
interface ActionItemSnippet {
|
|
229
|
+
id: string;
|
|
230
|
+
editedContent?: string; // User-edited version (if available)
|
|
231
|
+
aiGeneratedContent: string; // Decrypted AI-generated content
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Error Handling
|
|
236
|
+
|
|
237
|
+
The plugin normalizes HTTP errors into descriptive messages:
|
|
238
|
+
|
|
239
|
+
| Status Code | Error Message |
|
|
240
|
+
|-------------|---------------|
|
|
241
|
+
| 401 | `Authentication failed: Invalid or expired token` |
|
|
242
|
+
| 403 | `Access denied: User not authorized to view this summary` |
|
|
243
|
+
| 404 | `Container not found` |
|
|
244
|
+
| Other | `{methodName} failed: {error.message}` |
|
|
245
|
+
|
|
246
|
+
Validation errors are thrown synchronously:
|
|
247
|
+
- Missing or empty `containerId` throws `containerId is required and must be a non-empty string`
|
|
248
|
+
- Missing `containerInfo`, `summaryData` URL, or `encryptionKeyUrl` throws `containerInfo with valid summaryData and encryptionKeyUrl is required`
|
|
249
|
+
|
|
250
|
+
## Encryption / Decryption
|
|
251
|
+
|
|
252
|
+
All AI-generated content from the AI Bridge service is JWE-encrypted. Decryption uses:
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
webex.internal.encryption.decryptText(encryptionKeyUrl, encryptedContent)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
This requires:
|
|
259
|
+
1. A registered device (`webex.internal.device.register()`)
|
|
260
|
+
2. Mercury WebSocket connection (initiated automatically during KMS key fetch)
|
|
261
|
+
3. ECDHE key exchange with KMS
|
|
262
|
+
4. Key retrieval from KMS using the `encryptionKeyUrl`
|
|
263
|
+
|
|
264
|
+
The SDK handles steps 1-4 automatically when `decryptText` is called.
|
|
265
|
+
|
|
266
|
+
## Dependencies
|
|
267
|
+
|
|
268
|
+
- `@webex/webex-core` — Base plugin class, request handling, auth interceptor
|
|
269
|
+
- `@webex/internal-plugin-encryption` — KMS decryption
|
|
270
|
+
|
|
271
|
+
## Token Requirements
|
|
272
|
+
|
|
273
|
+
The Pragya and AI Bridge APIs require a valid Webex access token. The SDK's auth interceptor automatically attaches the token for URLs in the service catalog or on allowed domains (e.g., `wbx2.com`, `webex.com`).
|
|
274
|
+
|
|
275
|
+
## Manual Testing
|
|
276
|
+
|
|
277
|
+
Two manual test scripts are provided in `src/`:
|
|
278
|
+
|
|
279
|
+
### `manual-pragya-api-test.js`
|
|
280
|
+
Validates the Pragya container response structure (34 checks).
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
cd packages/@webex/internal-plugin-call-ai-summary
|
|
284
|
+
WEBEX_TOKEN='<token>' node src/manual-pragya-api-test.js
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### `manual-integration-test.js`
|
|
288
|
+
Tests the full end-to-end flow using the SDK service catalog:
|
|
289
|
+
1. Device registration (WDM) to populate the service catalog
|
|
290
|
+
2. `getContainer` via plugin (resolves `service: 'pragya'` from the catalog)
|
|
291
|
+
3. `getSummary` via plugin (fetches + decrypts note, short note, and action items via KMS)
|
|
292
|
+
4. `getTranscriptUrl` via plugin
|
|
293
|
+
5. Transcript content fetch
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
cd packages/@webex/internal-plugin-call-ai-summary
|
|
297
|
+
WEBEX_TOKEN='<token>' CONTAINER_ID='<id>' node src/manual-integration-test.js
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Both scripts require a valid Webex access token. Set `WEBEX_TOKEN` and optionally `CONTAINER_ID` as environment variables, or update the placeholder values in the scripts.
|