@ctchealth/plato-sdk 0.0.19 → 0.0.21

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/README.md CHANGED
@@ -17,7 +17,7 @@ All API requests require authentication via a client token. Pass a pre-signed JW
17
17
  - Comprehensive event system for call management
18
18
  - Type-safe API with full TypeScript support
19
19
  - Medical training simulation configuration
20
- - Simulation persistence and recovery across page reloads
20
+ - Simulation persistence across page reloads
21
21
 
22
22
  ## Installation
23
23
 
@@ -165,6 +165,8 @@ Creates a new medical training simulation. It may take a few minutes for the sim
165
165
 
166
166
  **Returns:** `Promise<{ simulationId: string, phase: CreationPhase }>`
167
167
 
168
+ **Learning mode (`isLearning`):** Pass `isLearning: true` and their calls are excluded from `getRecommendations()` and have no post call. Defaults to `false`.
169
+
168
170
  ##### startCall(simulationId: string)
169
171
 
170
172
  Starts a voice call with the AI persona for the specified simulation.
@@ -475,7 +477,7 @@ await client.deleteSimulation('507f1f77bcf86cd799439011');
475
477
 
476
478
  ##### getRecommendations()
477
479
 
478
- Retrieves recommendations based on the user's recent call performance. Analyzes the 5 most recent calls using a sliding window of 3 calls to identify patterns and provide actionable insights. Calls shorter than 2 minutes are excluded from analysis. No new recommendations are generated if fewer than 3 eligible calls are available.
480
+ Retrieves recommendations based on the user's recent call performance. Analyzes the 5 most recent calls using a sliding window of 3 calls to identify patterns and provide actionable insights. Calls shorter than 2 minutes are excluded from analysis, as are calls made against learning-mode simulations (`isLearning: true`). No new recommendations are generated if fewer than 3 eligible calls are available.
479
481
 
480
482
  **Returns:** `Promise<RecommendationsResponseDto>` — An object containing:
481
483
 
@@ -597,6 +599,7 @@ const inProgress = simulations.find(
597
599
  );
598
600
  ```
599
601
 
602
+ <!--
600
603
  ## Call Recovery
601
604
 
602
605
  The SDK automatically handles call recovery in case of page refreshes or browser closures during active calls. This ensures that call data is never lost and all calls get properly processed by the backend, even if the page is refreshed while a call is in progress.
@@ -898,6 +901,7 @@ A: The `recoverAbandonedCall()` method returns `true` when a call is recovered.
898
901
  **Q: What if the backend is down during recovery?**
899
902
 
900
903
  A: The recovery attempt is logged and the stored state is cleared to prevent infinite retries. The SDK continues working normally. Recovery failures are graceful and don't break the app.
904
+ -->
901
905
 
902
906
  ## Event System
903
907
 
@@ -912,6 +916,7 @@ The SDK provides a comprehensive event system for managing voice calls:
912
916
  - `message`: Triggered when a message is received (contains transcript and metadata)
913
917
  - `volume-level`: Triggered with volume level updates (number)
914
918
  - `error`: Triggered when an error occurs
919
+ - `call-ended-reason`: Triggered just before `call-end` with the reason the call was terminated (e.g. silence timeout). Use this to show the user a contextual message immediately
915
920
  - `call-details-ready`: Triggered when post-call processing completes and call details are available (includes full `CallDTO` with transcript, summary, ratings, and evaluation)
916
921
 
917
922
  ### Event Usage
@@ -937,6 +942,29 @@ call.on('call-details-ready', callDetails => {
937
942
  });
938
943
  ```
939
944
 
945
+ ## Detecting Why a Call Ended
946
+
947
+ The `call-ended-reason` event fires **just before** `call-end`, giving you the reason the call was terminated in time to update the UI before any post-call processing begins.
948
+
949
+ ### Usage
950
+
951
+ ```typescript
952
+ call.on('call-ended-reason', ({ reason, isInactivity }) => {
953
+ // reason — raw string from the platform, e.g. "silence-timed-out"
954
+ // isInactivity — true when reason contains "silence" or "inactivity"
955
+ });
956
+ ```
957
+
958
+ ### Known Reason Values
959
+
960
+ | `reason` | `isInactivity` | When it occurs |
961
+ | --------------------- | -------------- | ------------------------------------------------------- |
962
+ | `silence-timed-out` | `true` | No speech detected within the configured silence window |
963
+ | `customer-ended-call` | `false` | The user clicked "End Call" |
964
+ | `pipeline-error` | `false` | An internal platform error terminated the call |
965
+
966
+ > **Note:** The `reason` field reflects the raw value from the underlying voice platform. New values may be introduced over time. Always handle unknown reasons gracefully by falling back to a generic message.
967
+
940
968
  ## Automatic Post-Call Feedback
941
969
 
942
970
  The SDK automatically fetches detailed call information after each call ends and completes post-call processing. This includes comprehensive feedback such as transcript, summary, evaluation metrics, strengths, weaknesses, and ratings.
@@ -1116,10 +1144,22 @@ call.on('call-details-ready', callDetails => {
1116
1144
 
1117
1145
  ### Timing Considerations
1118
1146
 
1147
+ - `call-ended-reason`: Fires immediately before `call-end`, with the reason the call stopped
1119
1148
  - `call-end`: Fires immediately when the call stops
1120
1149
  - `call-details-ready`: Fires after backend processing completes (typically 2-5 seconds after call ends)
1121
1150
 
1122
- Plan your UX accordingly—show a loading/processing state between these two events.
1151
+ Plan your UX accordingly—show a contextual end message on `call-ended-reason`, then a loading/processing state until `call-details-ready` arrives.
1152
+
1153
+ ### Call Duration and Data Availability
1154
+
1155
+ The quality of data returned in `call-details-ready` depends on the duration of the call:
1156
+
1157
+ | Call duration | Behaviour |
1158
+ | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1159
+ | **< ~15 seconds** | `call-details-ready` fires immediately after `call-end` with whatever the database currently holds. Fields like `transcript`, `summary`, `score`, `strengths`, and `weaknesses` will typically be empty or zero — the call was too short to generate a meaningful analysis. |
1160
+ | **≥ ~15 seconds** | The SDK polls the backend until Vapi's post-call analysis is ready (transcript present), then emits `call-details-ready` with the fully populated `CallDTO`. |
1161
+
1162
+ > **Note:** The ~15 second threshold accounts for the Vapi connection handshake (typically 1–5 seconds) plus a minimum amount of conversation time required for analysis to be generated. Always handle empty fields gracefully regardless of call length.
1123
1163
 
1124
1164
  ## Data Types
1125
1165
 
@@ -1167,6 +1207,7 @@ interface CreateSimulationDto {
1167
1207
  imageId: string;
1168
1208
  avatarLanguage: AvatarLanguage;
1169
1209
  pdfSlideId?: string;
1210
+ isLearning?: boolean;
1170
1211
  }
1171
1212
  ```
1172
1213
 
@@ -1426,6 +1467,31 @@ try {
1426
1467
  }
1427
1468
  ```
1428
1469
 
1470
+ ### Concurrency Limit Error
1471
+
1472
+ When the platform has reached its maximum number of simultaneous active calls, `startCall()` throws a `CallConcurrencyLimitError`. This is a platform-wide limit, not user-specific. You should catch this specifically and show the user a clear message rather than a generic error.
1473
+
1474
+ ```typescript
1475
+ import { CallConcurrencyLimitError } from 'plato-sdk';
1476
+
1477
+ const handleStartCall = async (simulationId: string) => {
1478
+ try {
1479
+ const call = await platoClient.startCall(simulationId);
1480
+
1481
+ call.on('call-start', () => setCallActive(true));
1482
+ call.on('call-end', () => setCallActive(false));
1483
+ } catch (error) {
1484
+ if (error instanceof CallConcurrencyLimitError) {
1485
+ setErrorMessage(
1486
+ 'The platform has reached its maximum number of simultaneous calls. Please try again in a moment.'
1487
+ );
1488
+ } else {
1489
+ setErrorMessage('Failed to start call. Please try again.');
1490
+ }
1491
+ }
1492
+ };
1493
+ ```
1494
+
1429
1495
  ## Support
1430
1496
 
1431
1497
  For support and questions, please contact the development team.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctchealth/plato-sdk",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "dependencies": {
5
5
  "tslib": "^2.3.0",
6
6
  "@vapi-ai/web": "2.1.8",
@@ -12,5 +12,6 @@
12
12
  "typings": "./src/index.d.ts",
13
13
  "publishConfig": {
14
14
  "access": "public"
15
- }
16
- }
15
+ },
16
+ "types": "./src/index.d.ts"
17
+ }
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ /**
5
+ * Copyright (c) 2025 ctcHealth. All rights reserved.
6
+ *
7
+ * This file is part of the ctcHealth Plato Platform, a proprietary software system developed by ctcHealth.
8
+ *
9
+ * This source code and all related materials are confidential and proprietary to ctcHealth.
10
+ * Unauthorized access, use, copying, modification, distribution, or disclosure is strictly prohibited
11
+ * and may result in disciplinary action and civil and/or criminal penalties.
12
+ *
13
+ * This software is intended solely for authorized use within ctcHealth and its designated partners.
14
+ *
15
+ * For internal use only.
16
+ */
17
+ tslib_1.__exportStar(require("./lib/plato-sdk"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/plato-sdk/src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,0DAAgC"}
@@ -11,6 +11,6 @@
11
11
  *
12
12
  * For internal use only.
13
13
  */
14
- export const MAX_PDF_FILE_SIZE = 20 * 1024 * 1024; // 20MB
15
- export const ALLOWED_PDF_MIME_TYPES = ['application/pdf'];
16
- export const MAX_PDF_PAGES = 100;
14
+ export declare const MAX_PDF_FILE_SIZE: number;
15
+ export declare const ALLOWED_PDF_MIME_TYPES: string[];
16
+ export declare const MAX_PDF_PAGES = 100;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_PDF_PAGES = exports.ALLOWED_PDF_MIME_TYPES = exports.MAX_PDF_FILE_SIZE = void 0;
4
+ /**
5
+ * Copyright (c) 2025 ctcHealth. All rights reserved.
6
+ *
7
+ * This file is part of the ctcHealth Plato Platform, a proprietary software system developed by ctcHealth.
8
+ *
9
+ * This source code and all related materials are confidential and proprietary to ctcHealth.
10
+ * Unauthorized access, use, copying, modification, distribution, or disclosure is strictly prohibited
11
+ * and may result in disciplinary action and civil and/or criminal penalties.
12
+ *
13
+ * This software is intended solely for authorized use within ctcHealth and its designated partners.
14
+ *
15
+ * For internal use only.
16
+ */
17
+ exports.MAX_PDF_FILE_SIZE = 20 * 1024 * 1024; // 20MB
18
+ exports.ALLOWED_PDF_MIME_TYPES = ['application/pdf'];
19
+ exports.MAX_PDF_PAGES = 100;
20
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../libs/plato-sdk/src/lib/constants.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACU,QAAA,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAC7C,QAAA,sBAAsB,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAC7C,QAAA,aAAa,GAAG,GAAG,CAAC"}
@@ -0,0 +1,391 @@
1
+ /**
2
+ * Copyright (c) 2025 ctcHealth. All rights reserved.
3
+ *
4
+ * This file is part of the ctcHealth Plato Platform, a proprietary software system developed by ctcHealth.
5
+ *
6
+ * This source code and all related materials are confidential and proprietary to ctcHealth.
7
+ * Unauthorized access, use, copying, modification, distribution, or disclosure is strictly prohibited
8
+ * and may result in disciplinary action and civil and/or criminal penalties.
9
+ *
10
+ * This software is intended solely for authorized use within ctcHealth and its designated partners.
11
+ *
12
+ * For internal use only.
13
+ */
14
+ export declare enum YearOfExperience {
15
+ '1-5' = "1-5 years",
16
+ '6-10' = "6-10 years",
17
+ '11-20' = "16-20 years",
18
+ '20+' = "20+ years"
19
+ }
20
+ export declare enum PracticeType {
21
+ Private = "Private Practice",
22
+ Hospital = "Hospital",
23
+ AcademicMedicalCenter = "Academic Medical Center",
24
+ Clinic = "Clinic"
25
+ }
26
+ export declare enum SegmentType {
27
+ Traditionalist = "The Traditionalist",
28
+ Innovator = "The Innovator",
29
+ PatientOrientedPhysician = "The Patient-Oriented Physician",
30
+ FinanciallyDrivenPrescriber = "The Financially-Driven Prescriber",
31
+ EvidencePurist = "The Evidence-Purist",
32
+ CostConsciousPrescriber = "The Cost-Conscious Prescriber"
33
+ }
34
+ export declare enum AssistantVoiceGender {
35
+ Male = "Male",
36
+ Female = "Female"
37
+ }
38
+ export declare enum AvatarLanguage {
39
+ English = "en",
40
+ German = "de",
41
+ Spanish = "es",
42
+ Italian = "it",
43
+ French = "fr",
44
+ Arabic = "ar",
45
+ Russian = "ru"
46
+ }
47
+ export declare class PersonalityAndBehaviourDto {
48
+ riskTolerance: number;
49
+ researchOrientation: number;
50
+ recognitionNeed: number;
51
+ brandLoyalty: number;
52
+ patientEmpathy: number;
53
+ }
54
+ export declare class ProfessionalProfileDto {
55
+ practiceSettings: string;
56
+ yearOfExperience: number;
57
+ specialityAndDepartment: string;
58
+ location: string;
59
+ }
60
+ export declare class ContextDto {
61
+ subSpecialityOrTherapyFocus: string;
62
+ typicalPatientMix: string;
63
+ keyClinicalDrivers: string;
64
+ }
65
+ export declare class CharacterCreateDto {
66
+ name: string;
67
+ professionalProfile: ProfessionalProfileDto;
68
+ segment: SegmentType;
69
+ personalityAndBehaviour: PersonalityAndBehaviourDto;
70
+ context: ContextDto;
71
+ assistantGender?: AssistantVoiceGender;
72
+ }
73
+ export declare class ProductConfig {
74
+ name: string;
75
+ description: string;
76
+ }
77
+ export declare class CreateSimulationDto {
78
+ persona: CharacterCreateDto;
79
+ product: ProductConfig;
80
+ scenario: string;
81
+ objectives?: string;
82
+ anticipatedObjections?: string;
83
+ imageId: string;
84
+ avatarLanguage: AvatarLanguage;
85
+ pdfSlideId?: string;
86
+ isLearning?: boolean;
87
+ }
88
+ export type RecordingsLimit = 5 | 10 | 25;
89
+ export declare class SimulationRecordingsQueryDto {
90
+ limit?: RecordingsLimit;
91
+ page?: number;
92
+ sort?: SortOrder;
93
+ }
94
+ export declare enum SortOrder {
95
+ ASC = "asc",
96
+ DESC = "desc"
97
+ }
98
+ export declare enum RecordingStatus {
99
+ STARTED = "STARTED",
100
+ PROCESSING = "PROCESSING",
101
+ FINISHED = "FINISHED",
102
+ FAILED = "FAILED"
103
+ }
104
+ export declare class SimulationRecordingsDto {
105
+ _id: string;
106
+ createdAt: Date;
107
+ recordingStatus?: RecordingStatus;
108
+ }
109
+ export interface CallCreateDto {
110
+ /**
111
+ * Call ID obtained
112
+ */
113
+ callId: string;
114
+ /**
115
+ * Call Assistant ID
116
+ */
117
+ assistantId: string;
118
+ }
119
+ export interface CallDTO {
120
+ /**
121
+ * call ID
122
+ */
123
+ _id: string;
124
+ /**
125
+ * call ID obtained
126
+ */
127
+ callId: string;
128
+ /**
129
+ * call Assistant ID
130
+ */
131
+ assistantId: string;
132
+ /**
133
+ * call summary of the conversation
134
+ */
135
+ summary: string;
136
+ /**
137
+ * call transcript of the conversation
138
+ */
139
+ transcript: string;
140
+ /**
141
+ * call feedback provided by the user
142
+ */
143
+ feedback: string;
144
+ /**
145
+ * Success Evaluation returned by model
146
+ */
147
+ successEvaluation: boolean;
148
+ /**
149
+ * call Recording URL
150
+ */
151
+ recordingUrl: string;
152
+ /**
153
+ * Status of recording processing (e.g., 'STARTED', 'PROCESSING', 'FINISHED', 'FAILED')
154
+ */
155
+ recordingStatus?: RecordingStatus;
156
+ /**
157
+ * Date and Time of the creation of the message
158
+ */
159
+ createdAt: Date;
160
+ /**
161
+ * Date and Time of the creation of the message
162
+ */
163
+ endedAt: Date;
164
+ /**
165
+ * Rating of the call given by the user
166
+ */
167
+ rating: number;
168
+ /**
169
+ * Main strenghts of the user conversation based on the analysis of the AI
170
+ */
171
+ strengths: Array<string>;
172
+ /**
173
+ * Main weak points of the user conversation based on the analysis of the AI
174
+ */
175
+ weaknesses: Array<string>;
176
+ /**
177
+ * Name of Metric for the AI feedback report
178
+ */
179
+ metric1: string;
180
+ /**
181
+ * Name of Metric for the AI feedback report
182
+ */
183
+ metric2: string;
184
+ /**
185
+ * Name of Metric for the AI feedback report
186
+ */
187
+ metric3: string;
188
+ /**
189
+ * AI feedback value for Metric 1
190
+ */
191
+ metric1Value: number;
192
+ /**
193
+ * AI feedback value for Metric 2
194
+ */
195
+ metric2Value: number;
196
+ /**
197
+ * AI feedback value for Metric 3
198
+ */
199
+ metric3Value: number;
200
+ /**
201
+ * AI feedback value for the call score
202
+ */
203
+ score?: number;
204
+ /**
205
+ * Defines if the calls will be consider for the memory feature
206
+ */
207
+ inMemory: boolean;
208
+ /**
209
+ * Duration of the call in milliseconds
210
+ */
211
+ callDurationMs?: number;
212
+ }
213
+ export declare enum CreationPhase {
214
+ QUEUED = "QUEUED",
215
+ STARTING = "STARTING",
216
+ BUILD_HEADER = "BUILD_HEADER",
217
+ SEGMENT_SELECTED = "SEGMENT_SELECTED",
218
+ BUILD_CONTEXT = "BUILD_CONTEXT",
219
+ BUILD_PSYCHOGRAPHICS = "BUILD_PSYCHOGRAPHICS",
220
+ BUILD_OBJECTIVES = "BUILD_OBJECTIVES",
221
+ PERSONA_ASSEMBLED = "PERSONA_ASSEMBLED",
222
+ CORE = "CORE",
223
+ BOUNDARIES = "BOUNDARIES",
224
+ SPEECH_AND_THOUGHT = "SPEECH_AND_THOUGHT",
225
+ CONVERSATION_EVOLUTION = "CONVERSATION_EVOLUTION",
226
+ MEMORY = "MEMORY",
227
+ OBJECTION_HANDLING = "OBJECTION_HANDLING",
228
+ PERFORMANCE_EVALUATION = "PERFORMANCE_EVALUATION",
229
+ BEHAVIORAL_FRAMEWORKS = "BEHAVIORAL_FRAMEWORKS",
230
+ FINISHED = "FINISHED",
231
+ ERROR = "ERROR"
232
+ }
233
+ /**
234
+ * Presigned POST data for S3 upload.
235
+ */
236
+ export interface PresignedPost {
237
+ url: string;
238
+ fields: Record<string, string>;
239
+ }
240
+ /**
241
+ * Response for requesting a PDF upload.
242
+ */
243
+ export interface RequestPdfUploadResponse {
244
+ presignedPost: PresignedPost;
245
+ objectKey: string;
246
+ pdfId: string;
247
+ }
248
+ /**
249
+ * Persona description within simulation briefing
250
+ */
251
+ export interface PersonaDescriptionDto {
252
+ summary: string;
253
+ details: string[];
254
+ }
255
+ /**
256
+ * Scenario context within simulation briefing
257
+ */
258
+ export interface ScenarioContextDto {
259
+ summary: string;
260
+ steps: string[];
261
+ }
262
+ /**
263
+ * Training objectives within simulation briefing
264
+ */
265
+ export interface TrainingObjectiveDto {
266
+ summary: string;
267
+ keyObjectives: string[];
268
+ }
269
+ /**
270
+ * Complete simulation briefing structure generated by multi-agent system
271
+ */
272
+ export interface SimulationBriefingDto {
273
+ objectiveTag: string;
274
+ personaDescription: PersonaDescriptionDto;
275
+ scenarioContext: ScenarioContextDto;
276
+ trainingObjective: TrainingObjectiveDto;
277
+ }
278
+ export interface SimulationDetailsDto {
279
+ simulationId: string;
280
+ phase: CreationPhase;
281
+ assistantId: string;
282
+ configuration?: CreateSimulationDto;
283
+ simulationBriefing?: SimulationBriefingDto;
284
+ createdAt: Date;
285
+ updatedAt: Date;
286
+ }
287
+ export declare enum RecommendationPriority {
288
+ HIGH = "high",
289
+ MEDIUM = "medium",
290
+ LOW = "low"
291
+ }
292
+ export interface RecommendationItemDto {
293
+ title: string;
294
+ description: string;
295
+ priority: RecommendationPriority;
296
+ }
297
+ export interface PerformancePatternDto {
298
+ strength: string;
299
+ frequency: number;
300
+ }
301
+ export interface ImprovementAreaDto {
302
+ area: string;
303
+ frequency: number;
304
+ }
305
+ export interface RecommendationsResponseDto {
306
+ recommendations: RecommendationItemDto[];
307
+ strengths: PerformancePatternDto[];
308
+ improvementAreas: ImprovementAreaDto[];
309
+ summary: string;
310
+ callsAnalyzed: number;
311
+ generatedAt: Date;
312
+ }
313
+ export declare enum PdfSlidesStatus {
314
+ PENDING = "pending",
315
+ STARTED = "started",
316
+ PROCESSING = "processing",
317
+ OVERVIEW = "overview",
318
+ COMPLETED = "completed",
319
+ FAILED = "failed"
320
+ }
321
+ export interface CheckPdfStatusResponse {
322
+ status: PdfSlidesStatus;
323
+ processedBatches: number[];
324
+ totalBatches?: number;
325
+ }
326
+ export declare enum PdfSlidesSortField {
327
+ CREATED_AT = "createdAt",
328
+ FILENAME = "originalFilename",
329
+ TOTAL_SLIDES = "totalSlides"
330
+ }
331
+ export declare class PdfSlidesAnalysisQueryDto {
332
+ limit?: RecordingsLimit;
333
+ page?: number;
334
+ sort?: SortOrder;
335
+ sortBy?: PdfSlidesSortField;
336
+ }
337
+ export interface SlideAnalysis {
338
+ slideNumber: number;
339
+ title?: string;
340
+ textExtraction?: string;
341
+ visualDescription?: string;
342
+ }
343
+ export interface PdfSlidesDto {
344
+ _id: string;
345
+ status: PdfSlidesStatus;
346
+ originalFilename: string;
347
+ totalSlides?: number;
348
+ lessonOverview?: string;
349
+ createdAt: Date;
350
+ updatedAt: Date;
351
+ }
352
+ export interface PdfSlideDto {
353
+ _id: string;
354
+ status: PdfSlidesStatus;
355
+ originalFilename: string;
356
+ totalSlides?: number;
357
+ lessonOverview?: string;
358
+ slideAnalysis?: SlideAnalysis[];
359
+ createdAt: Date;
360
+ updatedAt: Date;
361
+ }
362
+ export interface AssistantImageDto {
363
+ _id: string;
364
+ imageUrl: string;
365
+ }
366
+ /**
367
+ * Represents the state of an active call stored in localStorage for recovery purposes.
368
+ * Used to detect and recover abandoned calls after page refresh.
369
+ */
370
+ export interface ActiveCallState {
371
+ /**
372
+ * MongoDB ID of the call record
373
+ */
374
+ callId: string;
375
+ /**
376
+ * External call provider ID
377
+ */
378
+ externalCallId: string;
379
+ /**
380
+ * ID of the simulation this call belongs to
381
+ */
382
+ simulationId: string;
383
+ /**
384
+ * ISO 8601 timestamp when the call was started
385
+ */
386
+ startedAt: string;
387
+ /**
388
+ * Schema version for future compatibility
389
+ */
390
+ version: number;
391
+ }