From 6c59b6420370e03b37a3e26728ad6b06ab28dfe3 Mon Sep 17 00:00:00 2001 From: sahil suman Date: Mon, 4 May 2026 16:49:21 +0530 Subject: [PATCH] feat: expose Daily recording events on Vapi client Forwards Daily's full set of recording events through the Vapi event emitter so consumers can react to the recording lifecycle without reaching into the underlying Daily call object: - recording-started - recording-stopped - recording-stats - recording-error - recording-upload-completed Each event is wired up in both `start()` and `reconnect()` and exposed with the corresponding `DailyEventObject*` type for type safety. Bumps version to 2.5.3. Refs: https://docs.daily.co/reference/daily-js/events/recording-events --- package-lock.json | 4 ++-- package.json | 2 +- vapi.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69622550b..69cb6584b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vapi-ai/web", - "version": "2.5.2", + "version": "2.5.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vapi-ai/web", - "version": "2.5.2", + "version": "2.5.3", "license": "MIT", "dependencies": { "@daily-co/daily-js": "^0.85.0", diff --git a/package.json b/package.json index 4e107c9ba..56ee61309 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vapi-ai/web", - "version": "2.5.2", + "version": "2.5.3", "description": "", "main": "dist/vapi.js", "types": "dist/vapi.d.ts", diff --git a/vapi.ts b/vapi.ts index e659313ff..d4ff1b9bc 100644 --- a/vapi.ts +++ b/vapi.ts @@ -5,7 +5,11 @@ import DailyIframe, { DailyAdvancedConfig, DailyFactoryOptions, DailyEventObjectAppMessage, + DailyEventObjectNoPayload, DailyEventObjectParticipant, + DailyEventObjectRecordingError, + DailyEventObjectRecordingStarted, + DailyEventObjectRecordingStopped, DailyEventObjectRemoteParticipantsAudioLevel, DailyParticipant, DailyVideoSendSettings, @@ -73,7 +77,12 @@ type VapiEventNames = | 'daily-participant-updated' | 'call-start-progress' | 'call-start-success' - | 'call-start-failed'; + | 'call-start-failed' + | 'recording-started' + | 'recording-stopped' + | 'recording-stats' + | 'recording-error' + | 'recording-upload-completed'; interface CallStartProgressEvent { stage: string; @@ -181,6 +190,11 @@ type VapiEventListeners = { 'call-start-progress': (event: CallStartProgressEvent) => void; 'call-start-success': (event: CallStartSuccessEvent) => void; 'call-start-failed': (event: CallStartFailedEvent) => void; + 'recording-started': (event: DailyEventObjectRecordingStarted) => void; + 'recording-stopped': (event: DailyEventObjectRecordingStopped) => void; + 'recording-stats': (event: DailyEventObjectNoPayload) => void; + 'recording-error': (event: DailyEventObjectRecordingError) => void; + 'recording-upload-completed': (event: DailyEventObjectNoPayload) => void; }; type StartCallOptions = { @@ -539,6 +553,26 @@ export default class Vapi extends VapiEventEmitter { this.emit('network-connection', event); }); + this.call.on('recording-started', (event) => { + if (event) this.emit('recording-started', event); + }); + + this.call.on('recording-stopped', (event) => { + if (event) this.emit('recording-stopped', event); + }); + + this.call.on('recording-stats', (event) => { + if (event) this.emit('recording-stats', event); + }); + + this.call.on('recording-error', (event) => { + if (event) this.emit('recording-error', event); + }); + + this.call.on('recording-upload-completed', (event) => { + if (event) this.emit('recording-upload-completed', event); + }); + this.call.on('track-started', async (e) => { if (!e || !e.participant) { return; @@ -1161,6 +1195,26 @@ export default class Vapi extends VapiEventEmitter { this.emit('network-connection', event); }); + this.call.on('recording-started', (event) => { + if (event) this.emit('recording-started', event); + }); + + this.call.on('recording-stopped', (event) => { + if (event) this.emit('recording-stopped', event); + }); + + this.call.on('recording-stats', (event) => { + if (event) this.emit('recording-stats', event); + }); + + this.call.on('recording-error', (event) => { + if (event) this.emit('recording-error', event); + }); + + this.call.on('recording-upload-completed', (event) => { + if (event) this.emit('recording-upload-completed', event); + }); + this.call.on('track-started', async (e) => { if (!e || !e.participant) { return;