@chanl/widget-sdk 0.2.0-canary.0
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 +257 -0
- package/dist/auth.d.ts +26 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +36 -0
- package/dist/auth.js.map +1 -0
- package/dist/chat/chat-client.d.ts +81 -0
- package/dist/chat/chat-client.d.ts.map +1 -0
- package/dist/chat/chat-client.js +192 -0
- package/dist/chat/chat-client.js.map +1 -0
- package/dist/chat/stream-parser.d.ts +20 -0
- package/dist/chat/stream-parser.d.ts.map +1 -0
- package/dist/chat/stream-parser.js +134 -0
- package/dist/chat/stream-parser.js.map +1 -0
- package/dist/chat/widget-config.d.ts +7 -0
- package/dist/chat/widget-config.d.ts.map +1 -0
- package/dist/chat/widget-config.js +26 -0
- package/dist/chat/widget-config.js.map +1 -0
- package/dist/client.d.ts +66 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +49 -0
- package/dist/client.js.map +1 -0
- package/dist/defaults.d.ts +12 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +27 -0
- package/dist/defaults.js.map +1 -0
- package/dist/embed/loader-types.d.ts +119 -0
- package/dist/embed/loader-types.d.ts.map +1 -0
- package/dist/embed/loader-types.js +20 -0
- package/dist/embed/loader-types.js.map +1 -0
- package/dist/embed/loader.d.ts +101 -0
- package/dist/embed/loader.d.ts.map +1 -0
- package/dist/embed/loader.js +439 -0
- package/dist/embed/loader.js.map +1 -0
- package/dist/embed/v1.global.js +5 -0
- package/dist/embed/v1.global.js.map +1 -0
- package/dist/events.d.ts +10 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +25 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +14 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/next/index.d.ts +58 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +83 -0
- package/dist/next/index.js.map +1 -0
- package/dist/react/index.d.ts +16 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +20 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +27 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +8 -0
- package/dist/react/types.js.map +1 -0
- package/dist/react/use-chanl.d.ts +27 -0
- package/dist/react/use-chanl.d.ts.map +1 -0
- package/dist/react/use-chanl.js +57 -0
- package/dist/react/use-chanl.js.map +1 -0
- package/dist/react/use-chat.d.ts +32 -0
- package/dist/react/use-chat.d.ts.map +1 -0
- package/dist/react/use-chat.js +224 -0
- package/dist/react/use-chat.js.map +1 -0
- package/dist/react/use-voice.d.ts +37 -0
- package/dist/react/use-voice.d.ts.map +1 -0
- package/dist/react/use-voice.js +268 -0
- package/dist/react/use-voice.js.map +1 -0
- package/dist/react/widget.d.ts +43 -0
- package/dist/react/widget.d.ts.map +1 -0
- package/dist/react/widget.js +188 -0
- package/dist/react/widget.js.map +1 -0
- package/dist/storage/session-storage.d.ts +48 -0
- package/dist/storage/session-storage.d.ts.map +1 -0
- package/dist/storage/session-storage.js +84 -0
- package/dist/storage/session-storage.js.map +1 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/voice/audio-recorder.d.ts +43 -0
- package/dist/voice/audio-recorder.d.ts.map +1 -0
- package/dist/voice/audio-recorder.js +127 -0
- package/dist/voice/audio-recorder.js.map +1 -0
- package/dist/voice/index.d.ts +13 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +16 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/mock-mode.d.ts +93 -0
- package/dist/voice/mock-mode.d.ts.map +1 -0
- package/dist/voice/mock-mode.js +375 -0
- package/dist/voice/mock-mode.js.map +1 -0
- package/dist/voice/transports/index.d.ts +5 -0
- package/dist/voice/transports/index.d.ts.map +1 -0
- package/dist/voice/transports/index.js +10 -0
- package/dist/voice/transports/index.js.map +1 -0
- package/dist/voice/transports/transport.d.ts +70 -0
- package/dist/voice/transports/transport.d.ts.map +1 -0
- package/dist/voice/transports/transport.js +12 -0
- package/dist/voice/transports/transport.js.map +1 -0
- package/dist/voice/transports/vapi.d.ts +147 -0
- package/dist/voice/transports/vapi.d.ts.map +1 -0
- package/dist/voice/transports/vapi.js +337 -0
- package/dist/voice/transports/vapi.js.map +1 -0
- package/dist/voice/transports/webrtc.d.ts +58 -0
- package/dist/voice/transports/webrtc.d.ts.map +1 -0
- package/dist/voice/transports/webrtc.js +318 -0
- package/dist/voice/transports/webrtc.js.map +1 -0
- package/dist/voice/transports/websocket.d.ts +39 -0
- package/dist/voice/transports/websocket.d.ts.map +1 -0
- package/dist/voice/transports/websocket.js +280 -0
- package/dist/voice/transports/websocket.js.map +1 -0
- package/dist/voice/types.d.ts +323 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +41 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/utils.d.ts +22 -0
- package/dist/voice/utils.d.ts.map +1 -0
- package/dist/voice/utils.js +44 -0
- package/dist/voice/utils.js.map +1 -0
- package/dist/voice/voice-client.d.ts +231 -0
- package/dist/voice/voice-client.d.ts.map +1 -0
- package/dist/voice/voice-client.js +1187 -0
- package/dist/voice/voice-client.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VapiTransport — voice transport that joins a VAPI Daily.co room.
|
|
3
|
+
*
|
|
4
|
+
* Unlike WebSocketTransport / WebRTCTransport which target our own backend,
|
|
5
|
+
* VapiTransport connects to a third-party VAPI session via a Daily.co webCallUrl
|
|
6
|
+
* returned by POST /realtime/session.
|
|
7
|
+
*
|
|
8
|
+
* Phase 1.1: connect/join + onReady + isConnected + `'vapi'` TransportType.
|
|
9
|
+
* Phase 1.2: audio controls (muteMic, unmuteMic, deafen, undeafen, setVolume)
|
|
10
|
+
* + full teardown (disconnect, destroy).
|
|
11
|
+
* Phase 1.3: VAPI app-message → SDK InboundServerMessage mapping.
|
|
12
|
+
*
|
|
13
|
+
* Output-volume strategy (Phase 1.2):
|
|
14
|
+
* @daily-co/daily-js@0.90.0 does NOT expose `setOutputVolume()` or any
|
|
15
|
+
* direct volume API. Daily auto-plays remote audio internally, but it fires
|
|
16
|
+
* a `track-started` event carrying the raw MediaStreamTrack. We intercept
|
|
17
|
+
* that track, attach it to a hidden <audio> element (same pattern as
|
|
18
|
+
* WebRTCTransport), and control `.muted` / `.volume` directly on the
|
|
19
|
+
* element. This is the only approach available in this SDK version that
|
|
20
|
+
* doesn't monkey-patch Daily internals.
|
|
21
|
+
*
|
|
22
|
+
* ── VAPI Daily app-message payload shapes (verified) ──────────────────────
|
|
23
|
+
*
|
|
24
|
+
* Source: @vapi-ai/web v2.5.2 source (packages/vapi.ts + api.ts)
|
|
25
|
+
* https://registry.npmjs.org/@vapi-ai/web/-/web-2.5.2.tgz
|
|
26
|
+
*
|
|
27
|
+
* The Daily `app-message` event fires as:
|
|
28
|
+
* call.on('app-message', (e: DailyEventObjectAppMessage) => ...)
|
|
29
|
+
* where e.data is either a plain string or a JSON-serialized string.
|
|
30
|
+
*
|
|
31
|
+
* Special plain-string sentinel:
|
|
32
|
+
* e.data === 'listening' → call has started (emitted as call-start in Vapi SDK)
|
|
33
|
+
*
|
|
34
|
+
* All other messages: JSON.parse(e.data) yields one of:
|
|
35
|
+
*
|
|
36
|
+
* TRANSCRIPT:
|
|
37
|
+
* { type: 'transcript', role: 'assistant'|'user',
|
|
38
|
+
* transcriptType: 'partial'|'final', transcript: string,
|
|
39
|
+
* timestamp?: number, isFiltered?: boolean }
|
|
40
|
+
* Source: ClientMessageTranscript in @vapi-ai/web api.ts
|
|
41
|
+
*
|
|
42
|
+
* SPEECH-UPDATE:
|
|
43
|
+
* { type: 'speech-update', status: 'started'|'stopped',
|
|
44
|
+
* role: 'assistant'|'user', turn?: number, timestamp?: number }
|
|
45
|
+
* Source: ClientMessageSpeechUpdate in @vapi-ai/web api.ts
|
|
46
|
+
*
|
|
47
|
+
* STATUS-UPDATE (call ended):
|
|
48
|
+
* { type: 'status-update', status: 'ended', endedReason?: string }
|
|
49
|
+
* The 'ended' status is the call-termination signal from VAPI.
|
|
50
|
+
* Source: ServerMessageStatusUpdate in @vapi-ai/web api.ts;
|
|
51
|
+
* confirmed via vapi.ts onAppMessage() hasEmittedCallEndedStatus logic.
|
|
52
|
+
*
|
|
53
|
+
* CONVERSATION-UPDATE, FUNCTION-CALL, HANG, MODEL-OUTPUT, TRANSFER-UPDATE,
|
|
54
|
+
* TOOL-CALLS, USER-INTERRUPTED, VOICE-INPUT, etc.:
|
|
55
|
+
* All other ClientMessage variants — not mapped to SDK messages (TODO if needed).
|
|
56
|
+
*
|
|
57
|
+
* SDK mapping notes:
|
|
58
|
+
* - TranscriptMessage has no partial/final flag → we map only 'final' transcripts.
|
|
59
|
+
* - TranscriptMessage.sender maps to VAPI's `role` field.
|
|
60
|
+
* - TranscriptMessage.text maps to VAPI's `transcript` field.
|
|
61
|
+
* - speech-update 'started' → SpeechStartMessage; 'stopped' → SpeechEndMessage.
|
|
62
|
+
* - status-update 'ended' → CallEndMessage (left-meeting handler also fires onClose).
|
|
63
|
+
*/
|
|
64
|
+
import type { DailyCall } from '@daily-co/daily-js';
|
|
65
|
+
import type { ITransport, TransportCallbacks, TransportConnectConfig } from './transport';
|
|
66
|
+
import type { OutboundClientMessage } from '../types';
|
|
67
|
+
type DailyFactory = () => DailyCall;
|
|
68
|
+
export declare class VapiTransport implements ITransport {
|
|
69
|
+
private readonly callbacks;
|
|
70
|
+
private readonly dailyFactory;
|
|
71
|
+
private call;
|
|
72
|
+
private joined;
|
|
73
|
+
private debug;
|
|
74
|
+
private audioDeafened;
|
|
75
|
+
private audioVolume;
|
|
76
|
+
private audioEl;
|
|
77
|
+
/**
|
|
78
|
+
* @param callbacks — transport event callbacks (onReady, onMessage, onError, onClose)
|
|
79
|
+
* @param dailyFactory — factory for creating the Daily call object; defaults to
|
|
80
|
+
* DailyIframe.createCallObject(). Injectable for tests.
|
|
81
|
+
*/
|
|
82
|
+
constructor(callbacks: TransportCallbacks, dailyFactory?: DailyFactory);
|
|
83
|
+
connect(config: TransportConnectConfig): Promise<void>;
|
|
84
|
+
disconnect(_code?: number, _reason?: string): void;
|
|
85
|
+
destroy(): void;
|
|
86
|
+
isConnected(): boolean;
|
|
87
|
+
sendMessage(message: OutboundClientMessage): void;
|
|
88
|
+
muteMic(): void;
|
|
89
|
+
unmuteMic(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Silence remote audio output.
|
|
92
|
+
* Sets `audioEl.muted = true` if the remote track element is already
|
|
93
|
+
* attached; saves state so it applies to any late-arriving track.
|
|
94
|
+
*/
|
|
95
|
+
deafen(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Restore remote audio output.
|
|
98
|
+
*/
|
|
99
|
+
undeafen(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Set remote output volume (0.0–1.0).
|
|
102
|
+
* Clamps to [0, 1]. State is saved so late-arriving tracks pick it up.
|
|
103
|
+
*/
|
|
104
|
+
setVolume(volume: number): void;
|
|
105
|
+
/** No-op — WebRTC/Daily has no client-side jitter buffer to flush. */
|
|
106
|
+
clearAudioBuffer(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Attach a remote MediaStreamTrack to a hidden <audio> element so we can
|
|
109
|
+
* control playback volume and muted state via standard DOM APIs.
|
|
110
|
+
*
|
|
111
|
+
* Why a hidden <audio> element?
|
|
112
|
+
* Daily JS 0.90.0 has no `setOutputVolume()` API. Daily auto-plays remote
|
|
113
|
+
* audio internally, but it exposes the raw MediaStreamTrack via the
|
|
114
|
+
* `track-started` event. By piping that track through our own <audio>
|
|
115
|
+
* element we get full DOM control (muted, volume) without touching Daily
|
|
116
|
+
* internals. The element is intentionally detached from layout via
|
|
117
|
+
* fixed + off-screen positioning (same as WebRTCTransport).
|
|
118
|
+
*/
|
|
119
|
+
private attachRemoteAudioTrack;
|
|
120
|
+
/** Remove the audio element from the DOM and release it. */
|
|
121
|
+
private _detachAudioEl;
|
|
122
|
+
/**
|
|
123
|
+
* Map a VAPI Daily app-message payload to an SDK InboundServerMessage and
|
|
124
|
+
* forward it via `this.callbacks.onMessage(...)`.
|
|
125
|
+
*
|
|
126
|
+
* VAPI sends app-message events in two forms (verified from @vapi-ai/web v2.5.2):
|
|
127
|
+
*
|
|
128
|
+
* 1. Plain string `'listening'` — call-start sentinel; ignored here because
|
|
129
|
+
* `joined-meeting` already fires `onReady()`.
|
|
130
|
+
*
|
|
131
|
+
* 2. JSON string — one of the ClientMessage variants documented above.
|
|
132
|
+
* We parse it and map the known types; unknown types are silently ignored.
|
|
133
|
+
*
|
|
134
|
+
* Mapping table:
|
|
135
|
+
* VAPI type | VAPI condition | SDK message
|
|
136
|
+
* ──────────────────────────────────────────────────────────────────
|
|
137
|
+
* 'transcript' | transcriptType === 'final' | TranscriptMessage
|
|
138
|
+
* 'transcript' | transcriptType === 'partial'| ignored (SDK has no partial flag)
|
|
139
|
+
* 'speech-update' | status === 'started' | SpeechStartMessage
|
|
140
|
+
* 'speech-update' | status === 'stopped' | SpeechEndMessage
|
|
141
|
+
* 'status-update' | status === 'ended' | CallEndMessage
|
|
142
|
+
* everything else | — | ignored
|
|
143
|
+
*/
|
|
144
|
+
private handleAppMessage;
|
|
145
|
+
}
|
|
146
|
+
export {};
|
|
147
|
+
//# sourceMappingURL=vapi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vapi.d.ts","sourceRoot":"","sources":["../../../src/voice/transports/vapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,UAAU,CAAC;AAGlB,KAAK,YAAY,GAAG,MAAM,SAAS,CAAC;AAEpC,qBAAa,aAAc,YAAW,UAAU;IAkB5C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAlB/B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IAGtB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAK;IAGxB,OAAO,CAAC,OAAO,CAAiC;IAEhD;;;;OAIG;gBAEgB,SAAS,EAAE,kBAAkB,EAC7B,YAAY,GAAE,YAA6C;IAKxE,OAAO,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwE5D,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAOlD,OAAO,IAAI,IAAI;IAcf,WAAW,IAAI,OAAO;IAMtB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAMjD,OAAO,IAAI,IAAI;IAIT,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,QAAQ,IAAI,IAAI;IAKhB;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,sEAAsE;IACtE,gBAAgB,IAAI,IAAI;IAIxB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sBAAsB;IAqB9B,4DAA4D;IAC5D,OAAO,CAAC,cAAc;IAStB;;;;;;;;;;;;;;;;;;;;;OAqBG;IAEH,OAAO,CAAC,gBAAgB;CA0DzB"}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VapiTransport — voice transport that joins a VAPI Daily.co room.
|
|
4
|
+
*
|
|
5
|
+
* Unlike WebSocketTransport / WebRTCTransport which target our own backend,
|
|
6
|
+
* VapiTransport connects to a third-party VAPI session via a Daily.co webCallUrl
|
|
7
|
+
* returned by POST /realtime/session.
|
|
8
|
+
*
|
|
9
|
+
* Phase 1.1: connect/join + onReady + isConnected + `'vapi'` TransportType.
|
|
10
|
+
* Phase 1.2: audio controls (muteMic, unmuteMic, deafen, undeafen, setVolume)
|
|
11
|
+
* + full teardown (disconnect, destroy).
|
|
12
|
+
* Phase 1.3: VAPI app-message → SDK InboundServerMessage mapping.
|
|
13
|
+
*
|
|
14
|
+
* Output-volume strategy (Phase 1.2):
|
|
15
|
+
* @daily-co/daily-js@0.90.0 does NOT expose `setOutputVolume()` or any
|
|
16
|
+
* direct volume API. Daily auto-plays remote audio internally, but it fires
|
|
17
|
+
* a `track-started` event carrying the raw MediaStreamTrack. We intercept
|
|
18
|
+
* that track, attach it to a hidden <audio> element (same pattern as
|
|
19
|
+
* WebRTCTransport), and control `.muted` / `.volume` directly on the
|
|
20
|
+
* element. This is the only approach available in this SDK version that
|
|
21
|
+
* doesn't monkey-patch Daily internals.
|
|
22
|
+
*
|
|
23
|
+
* ── VAPI Daily app-message payload shapes (verified) ──────────────────────
|
|
24
|
+
*
|
|
25
|
+
* Source: @vapi-ai/web v2.5.2 source (packages/vapi.ts + api.ts)
|
|
26
|
+
* https://registry.npmjs.org/@vapi-ai/web/-/web-2.5.2.tgz
|
|
27
|
+
*
|
|
28
|
+
* The Daily `app-message` event fires as:
|
|
29
|
+
* call.on('app-message', (e: DailyEventObjectAppMessage) => ...)
|
|
30
|
+
* where e.data is either a plain string or a JSON-serialized string.
|
|
31
|
+
*
|
|
32
|
+
* Special plain-string sentinel:
|
|
33
|
+
* e.data === 'listening' → call has started (emitted as call-start in Vapi SDK)
|
|
34
|
+
*
|
|
35
|
+
* All other messages: JSON.parse(e.data) yields one of:
|
|
36
|
+
*
|
|
37
|
+
* TRANSCRIPT:
|
|
38
|
+
* { type: 'transcript', role: 'assistant'|'user',
|
|
39
|
+
* transcriptType: 'partial'|'final', transcript: string,
|
|
40
|
+
* timestamp?: number, isFiltered?: boolean }
|
|
41
|
+
* Source: ClientMessageTranscript in @vapi-ai/web api.ts
|
|
42
|
+
*
|
|
43
|
+
* SPEECH-UPDATE:
|
|
44
|
+
* { type: 'speech-update', status: 'started'|'stopped',
|
|
45
|
+
* role: 'assistant'|'user', turn?: number, timestamp?: number }
|
|
46
|
+
* Source: ClientMessageSpeechUpdate in @vapi-ai/web api.ts
|
|
47
|
+
*
|
|
48
|
+
* STATUS-UPDATE (call ended):
|
|
49
|
+
* { type: 'status-update', status: 'ended', endedReason?: string }
|
|
50
|
+
* The 'ended' status is the call-termination signal from VAPI.
|
|
51
|
+
* Source: ServerMessageStatusUpdate in @vapi-ai/web api.ts;
|
|
52
|
+
* confirmed via vapi.ts onAppMessage() hasEmittedCallEndedStatus logic.
|
|
53
|
+
*
|
|
54
|
+
* CONVERSATION-UPDATE, FUNCTION-CALL, HANG, MODEL-OUTPUT, TRANSFER-UPDATE,
|
|
55
|
+
* TOOL-CALLS, USER-INTERRUPTED, VOICE-INPUT, etc.:
|
|
56
|
+
* All other ClientMessage variants — not mapped to SDK messages (TODO if needed).
|
|
57
|
+
*
|
|
58
|
+
* SDK mapping notes:
|
|
59
|
+
* - TranscriptMessage has no partial/final flag → we map only 'final' transcripts.
|
|
60
|
+
* - TranscriptMessage.sender maps to VAPI's `role` field.
|
|
61
|
+
* - TranscriptMessage.text maps to VAPI's `transcript` field.
|
|
62
|
+
* - speech-update 'started' → SpeechStartMessage; 'stopped' → SpeechEndMessage.
|
|
63
|
+
* - status-update 'ended' → CallEndMessage (left-meeting handler also fires onClose).
|
|
64
|
+
*/
|
|
65
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
66
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
67
|
+
};
|
|
68
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
69
|
+
exports.VapiTransport = void 0;
|
|
70
|
+
const daily_js_1 = __importDefault(require("@daily-co/daily-js"));
|
|
71
|
+
class VapiTransport {
|
|
72
|
+
/**
|
|
73
|
+
* @param callbacks — transport event callbacks (onReady, onMessage, onError, onClose)
|
|
74
|
+
* @param dailyFactory — factory for creating the Daily call object; defaults to
|
|
75
|
+
* DailyIframe.createCallObject(). Injectable for tests.
|
|
76
|
+
*/
|
|
77
|
+
constructor(callbacks, dailyFactory = () => daily_js_1.default.createCallObject()) {
|
|
78
|
+
this.callbacks = callbacks;
|
|
79
|
+
this.dailyFactory = dailyFactory;
|
|
80
|
+
this.call = null;
|
|
81
|
+
this.joined = false;
|
|
82
|
+
this.debug = false;
|
|
83
|
+
// Output-audio state — tracked so we can apply to late-arriving tracks
|
|
84
|
+
this.audioDeafened = false;
|
|
85
|
+
this.audioVolume = 1;
|
|
86
|
+
// Hidden <audio> element for remote participant output
|
|
87
|
+
this.audioEl = null;
|
|
88
|
+
}
|
|
89
|
+
// ── ITransport: connect ────────────────────────────────────────────────
|
|
90
|
+
async connect(config) {
|
|
91
|
+
this.debug = config.debug ?? false;
|
|
92
|
+
// Resolve the join URL.
|
|
93
|
+
// - A formal `joinUrl` field on TransportConnectConfig is added in Phase 1.4.
|
|
94
|
+
// Until then we read it from the roomState cast or a top-level field.
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
const roomState = config.roomState;
|
|
97
|
+
const joinUrl = config.joinUrl ?? roomState?.joinUrl ?? roomState?.webCallUrl;
|
|
98
|
+
if (!joinUrl) {
|
|
99
|
+
throw new Error('[VapiTransport] No join URL provided in connect config');
|
|
100
|
+
}
|
|
101
|
+
if (this.debug) {
|
|
102
|
+
console.debug('[VapiTransport] Connecting to Daily room:', joinUrl);
|
|
103
|
+
}
|
|
104
|
+
const call = this.dailyFactory();
|
|
105
|
+
this.call = call;
|
|
106
|
+
// Wire up lifecycle events before joining
|
|
107
|
+
call.on('joined-meeting', () => {
|
|
108
|
+
this.joined = true;
|
|
109
|
+
if (this.debug)
|
|
110
|
+
console.debug('[VapiTransport] joined-meeting');
|
|
111
|
+
this.callbacks.onReady();
|
|
112
|
+
});
|
|
113
|
+
call.on('left-meeting', () => {
|
|
114
|
+
this.joined = false;
|
|
115
|
+
if (this.debug)
|
|
116
|
+
console.debug('[VapiTransport] left-meeting');
|
|
117
|
+
this.callbacks.onClose(1000, 'left');
|
|
118
|
+
});
|
|
119
|
+
call.on('error', (event) => {
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
121
|
+
const msg = event?.errorMsg ?? 'daily error';
|
|
122
|
+
if (this.debug)
|
|
123
|
+
console.debug('[VapiTransport] error', msg);
|
|
124
|
+
this.callbacks.onError(new Error(msg));
|
|
125
|
+
});
|
|
126
|
+
// VAPI conversation events arrive as Daily app-message events.
|
|
127
|
+
// e.data is either the plain string 'listening' (call-start sentinel)
|
|
128
|
+
// or a JSON-serialized ClientMessage from the VAPI backend.
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
+
call.on('app-message', (e) => this.handleAppMessage(e?.data));
|
|
131
|
+
// Remote audio output: intercept the agent's audio track and play it
|
|
132
|
+
// via a hidden <audio> element so we can control volume / muted.
|
|
133
|
+
// Daily fires track-started for every participant track; we only care
|
|
134
|
+
// about non-local audio tracks (i.e. the VAPI agent).
|
|
135
|
+
call.on('track-started', (event) => {
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
|
+
const ev = event;
|
|
138
|
+
if (ev?.type !== 'audio')
|
|
139
|
+
return;
|
|
140
|
+
// Skip local participant's own track
|
|
141
|
+
if (ev?.participant?.local)
|
|
142
|
+
return;
|
|
143
|
+
const track = ev?.track;
|
|
144
|
+
if (!track)
|
|
145
|
+
return;
|
|
146
|
+
if (this.debug)
|
|
147
|
+
console.debug('[VapiTransport] Remote audio track started');
|
|
148
|
+
this.attachRemoteAudioTrack(track);
|
|
149
|
+
});
|
|
150
|
+
await call.join({ url: joinUrl });
|
|
151
|
+
}
|
|
152
|
+
// ── ITransport: disconnect / destroy ──────────────────────────────────
|
|
153
|
+
disconnect(_code, _reason) {
|
|
154
|
+
if (this.call) {
|
|
155
|
+
this.call.leave().catch(() => { });
|
|
156
|
+
this.joined = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
destroy() {
|
|
160
|
+
this._detachAudioEl();
|
|
161
|
+
if (this.call) {
|
|
162
|
+
const c = this.call;
|
|
163
|
+
this.call = null;
|
|
164
|
+
this.joined = false;
|
|
165
|
+
c.leave()
|
|
166
|
+
.catch(() => { })
|
|
167
|
+
.finally(() => c.destroy().catch(() => { }));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ── ITransport: isConnected ────────────────────────────────────────────
|
|
171
|
+
isConnected() {
|
|
172
|
+
return this.joined;
|
|
173
|
+
}
|
|
174
|
+
// ── ITransport: messaging ─────────────────────────────────────────────
|
|
175
|
+
sendMessage(message) {
|
|
176
|
+
this.call?.sendAppMessage(message);
|
|
177
|
+
}
|
|
178
|
+
// ── ITransport: audio controls ────────────────────────────────────────
|
|
179
|
+
muteMic() {
|
|
180
|
+
this.call?.setLocalAudio(false);
|
|
181
|
+
}
|
|
182
|
+
async unmuteMic() {
|
|
183
|
+
this.call?.setLocalAudio(true);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Silence remote audio output.
|
|
187
|
+
* Sets `audioEl.muted = true` if the remote track element is already
|
|
188
|
+
* attached; saves state so it applies to any late-arriving track.
|
|
189
|
+
*/
|
|
190
|
+
deafen() {
|
|
191
|
+
this.audioDeafened = true;
|
|
192
|
+
if (this.audioEl)
|
|
193
|
+
this.audioEl.muted = true;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Restore remote audio output.
|
|
197
|
+
*/
|
|
198
|
+
undeafen() {
|
|
199
|
+
this.audioDeafened = false;
|
|
200
|
+
if (this.audioEl)
|
|
201
|
+
this.audioEl.muted = false;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Set remote output volume (0.0–1.0).
|
|
205
|
+
* Clamps to [0, 1]. State is saved so late-arriving tracks pick it up.
|
|
206
|
+
*/
|
|
207
|
+
setVolume(volume) {
|
|
208
|
+
this.audioVolume = Math.max(0, Math.min(1, volume));
|
|
209
|
+
if (this.audioEl)
|
|
210
|
+
this.audioEl.volume = this.audioVolume;
|
|
211
|
+
}
|
|
212
|
+
/** No-op — WebRTC/Daily has no client-side jitter buffer to flush. */
|
|
213
|
+
clearAudioBuffer() { }
|
|
214
|
+
// ── Private helpers ───────────────────────────────────────────────────
|
|
215
|
+
/**
|
|
216
|
+
* Attach a remote MediaStreamTrack to a hidden <audio> element so we can
|
|
217
|
+
* control playback volume and muted state via standard DOM APIs.
|
|
218
|
+
*
|
|
219
|
+
* Why a hidden <audio> element?
|
|
220
|
+
* Daily JS 0.90.0 has no `setOutputVolume()` API. Daily auto-plays remote
|
|
221
|
+
* audio internally, but it exposes the raw MediaStreamTrack via the
|
|
222
|
+
* `track-started` event. By piping that track through our own <audio>
|
|
223
|
+
* element we get full DOM control (muted, volume) without touching Daily
|
|
224
|
+
* internals. The element is intentionally detached from layout via
|
|
225
|
+
* fixed + off-screen positioning (same as WebRTCTransport).
|
|
226
|
+
*/
|
|
227
|
+
attachRemoteAudioTrack(track) {
|
|
228
|
+
// Reuse the element if already created
|
|
229
|
+
if (!this.audioEl) {
|
|
230
|
+
const el = document.createElement('audio');
|
|
231
|
+
el.autoplay = true;
|
|
232
|
+
el.setAttribute('playsinline', 'true');
|
|
233
|
+
// Keep off-screen so it never affects layout
|
|
234
|
+
el.style.position = 'fixed';
|
|
235
|
+
el.style.top = '-9999px';
|
|
236
|
+
document.body.appendChild(el);
|
|
237
|
+
this.audioEl = el;
|
|
238
|
+
}
|
|
239
|
+
this.audioEl.srcObject = new MediaStream([track]);
|
|
240
|
+
this.audioEl.muted = this.audioDeafened;
|
|
241
|
+
this.audioEl.volume = this.audioVolume;
|
|
242
|
+
this.audioEl.play().catch((err) => {
|
|
243
|
+
if (this.debug)
|
|
244
|
+
console.debug('[VapiTransport] audio.play() rejected:', err);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/** Remove the audio element from the DOM and release it. */
|
|
248
|
+
_detachAudioEl() {
|
|
249
|
+
if (this.audioEl) {
|
|
250
|
+
this.audioEl.pause();
|
|
251
|
+
this.audioEl.srcObject = null;
|
|
252
|
+
this.audioEl.parentNode?.removeChild(this.audioEl);
|
|
253
|
+
this.audioEl = null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Map a VAPI Daily app-message payload to an SDK InboundServerMessage and
|
|
258
|
+
* forward it via `this.callbacks.onMessage(...)`.
|
|
259
|
+
*
|
|
260
|
+
* VAPI sends app-message events in two forms (verified from @vapi-ai/web v2.5.2):
|
|
261
|
+
*
|
|
262
|
+
* 1. Plain string `'listening'` — call-start sentinel; ignored here because
|
|
263
|
+
* `joined-meeting` already fires `onReady()`.
|
|
264
|
+
*
|
|
265
|
+
* 2. JSON string — one of the ClientMessage variants documented above.
|
|
266
|
+
* We parse it and map the known types; unknown types are silently ignored.
|
|
267
|
+
*
|
|
268
|
+
* Mapping table:
|
|
269
|
+
* VAPI type | VAPI condition | SDK message
|
|
270
|
+
* ──────────────────────────────────────────────────────────────────
|
|
271
|
+
* 'transcript' | transcriptType === 'final' | TranscriptMessage
|
|
272
|
+
* 'transcript' | transcriptType === 'partial'| ignored (SDK has no partial flag)
|
|
273
|
+
* 'speech-update' | status === 'started' | SpeechStartMessage
|
|
274
|
+
* 'speech-update' | status === 'stopped' | SpeechEndMessage
|
|
275
|
+
* 'status-update' | status === 'ended' | CallEndMessage
|
|
276
|
+
* everything else | — | ignored
|
|
277
|
+
*/
|
|
278
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
|
+
handleAppMessage(data) {
|
|
280
|
+
// Plain-string 'listening' sentinel — call-start is already handled by
|
|
281
|
+
// joined-meeting → onReady(). Nothing else to do.
|
|
282
|
+
if (data === 'listening' || typeof data !== 'string')
|
|
283
|
+
return;
|
|
284
|
+
let msg;
|
|
285
|
+
try {
|
|
286
|
+
msg = JSON.parse(data);
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
if (this.debug)
|
|
290
|
+
console.debug('[VapiTransport] app-message: failed to parse JSON', data);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const type = msg.type;
|
|
294
|
+
if (!type)
|
|
295
|
+
return;
|
|
296
|
+
if (this.debug)
|
|
297
|
+
console.debug('[VapiTransport] app-message:', type, msg);
|
|
298
|
+
switch (type) {
|
|
299
|
+
case 'transcript': {
|
|
300
|
+
// Only forward final transcripts — SDK TranscriptMessage has no partial flag.
|
|
301
|
+
if (msg.transcriptType !== 'final')
|
|
302
|
+
return;
|
|
303
|
+
const sdkMsg = {
|
|
304
|
+
type: 'transcript',
|
|
305
|
+
text: msg.transcript,
|
|
306
|
+
sender: msg.role ?? 'assistant',
|
|
307
|
+
timestamp: typeof msg.timestamp === 'number' ? msg.timestamp : Date.now(),
|
|
308
|
+
};
|
|
309
|
+
this.callbacks.onMessage(sdkMsg);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case 'speech-update': {
|
|
313
|
+
const sdkMsg = msg.status === 'started'
|
|
314
|
+
? { type: 'speech-start' }
|
|
315
|
+
: { type: 'speech-end' };
|
|
316
|
+
this.callbacks.onMessage(sdkMsg);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case 'status-update': {
|
|
320
|
+
// 'ended' is the VAPI call-termination signal.
|
|
321
|
+
// The left-meeting event fires onClose(); this forwards the SDK call-end
|
|
322
|
+
// message so voice-client can update its state machine.
|
|
323
|
+
if (msg.status === 'ended') {
|
|
324
|
+
const sdkMsg = { type: 'call-end' };
|
|
325
|
+
this.callbacks.onMessage(sdkMsg);
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
// conversation-update, function-call, hang, model-output, transfer-update,
|
|
330
|
+
// tool-calls, user-interrupted, voice-input, etc. — not mapped (TODO).
|
|
331
|
+
default:
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
exports.VapiTransport = VapiTransport;
|
|
337
|
+
//# sourceMappingURL=vapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vapi.js","sourceRoot":"","sources":["../../../src/voice/transports/vapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebRTCTransport — native browser WebRTC for voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Audio path:
|
|
5
|
+
* Mic → getUserMedia → addTrack(pc) → RTP to server (no base64, no encoding)
|
|
6
|
+
* Server → remote audio track → <audio> element (native playback)
|
|
7
|
+
*
|
|
8
|
+
* Messages (transcripts, config, events) go through RTCDataChannel.
|
|
9
|
+
* Signaling uses HTTP POST /offer + /candidates to the voice-bot-sdk.
|
|
10
|
+
*
|
|
11
|
+
* Advantages over WebSocket:
|
|
12
|
+
* - No base64 encoding overhead (33% bandwidth savings)
|
|
13
|
+
* - Native echo cancellation + noise suppression
|
|
14
|
+
* - Lower latency (no JSON framing per audio chunk)
|
|
15
|
+
* - Browser-native audio playback (no PCMPlayer)
|
|
16
|
+
*/
|
|
17
|
+
import type { ITransport, TransportCallbacks, TransportConnectConfig } from './transport';
|
|
18
|
+
import type { OutboundClientMessage } from '../types';
|
|
19
|
+
export declare class WebRTCTransport implements ITransport {
|
|
20
|
+
private pc;
|
|
21
|
+
private dc;
|
|
22
|
+
private stream;
|
|
23
|
+
private audioEl;
|
|
24
|
+
private pingInterval;
|
|
25
|
+
private micMuted;
|
|
26
|
+
private audioDeafened;
|
|
27
|
+
private debug;
|
|
28
|
+
private callbacks;
|
|
29
|
+
private signalingUrl;
|
|
30
|
+
/**
|
|
31
|
+
* @param callbacks — transport event callbacks
|
|
32
|
+
* @param signalingUrl — platform API base URL for signaling relay.
|
|
33
|
+
* The transport POSTs to `{signalingUrl}/offer/{interactionId}` and
|
|
34
|
+
* `{signalingUrl}/candidates`.
|
|
35
|
+
*/
|
|
36
|
+
constructor(callbacks: TransportCallbacks, signalingUrl: string);
|
|
37
|
+
connect(config: TransportConnectConfig): Promise<void>;
|
|
38
|
+
disconnect(_code?: number, _reason?: string): void;
|
|
39
|
+
sendMessage(message: OutboundClientMessage): void;
|
|
40
|
+
isConnected(): boolean;
|
|
41
|
+
muteMic(): void;
|
|
42
|
+
unmuteMic(): Promise<void>;
|
|
43
|
+
deafen(): void;
|
|
44
|
+
undeafen(): void;
|
|
45
|
+
setVolume(volume: number): void;
|
|
46
|
+
clearAudioBuffer(): void;
|
|
47
|
+
destroy(): void;
|
|
48
|
+
private handleDataChannelMessage;
|
|
49
|
+
/**
|
|
50
|
+
* Convert pipecat RTVI-format data channel messages to Audial's message format.
|
|
51
|
+
*
|
|
52
|
+
* RTVI sends: {label:"rtvi-ai", type:"user-transcription"|"bot-transcription",
|
|
53
|
+
* data:{text:"...", user_id:"", timestamp:"..."}}
|
|
54
|
+
* Audial expects: {type:"transcript", text:"...", sender:"user"|"assistant", timestamp:123}
|
|
55
|
+
*/
|
|
56
|
+
private convertRTVIMessage;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=webrtc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../../../src/voice/transports/webrtc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,UAAU,CAAC;AAElB,qBAAa,eAAgB,YAAW,UAAU;IAChD,OAAO,CAAC,EAAE,CAAkC;IAC5C,OAAO,CAAC,EAAE,CAA+B;IACzC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,YAAY,CAAS;IAE7B;;;;;OAKG;gBACS,SAAS,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM;IAOzD,OAAO,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkK5D,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAOlD,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAcjD,WAAW,IAAI,OAAO;IAMtB,OAAO,IAAI,IAAI;IAMT,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAMhC,MAAM,IAAI,IAAI;IAMd,QAAQ,IAAI,IAAI;IAMhB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,gBAAgB,IAAI,IAAI;IAMxB,OAAO,IAAI,IAAI;IA0Bf,OAAO,CAAC,wBAAwB;IA6BhC;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;CAoB3B"}
|