@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.
Files changed (128) hide show
  1. package/README.md +257 -0
  2. package/dist/auth.d.ts +26 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +36 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/chat/chat-client.d.ts +81 -0
  7. package/dist/chat/chat-client.d.ts.map +1 -0
  8. package/dist/chat/chat-client.js +192 -0
  9. package/dist/chat/chat-client.js.map +1 -0
  10. package/dist/chat/stream-parser.d.ts +20 -0
  11. package/dist/chat/stream-parser.d.ts.map +1 -0
  12. package/dist/chat/stream-parser.js +134 -0
  13. package/dist/chat/stream-parser.js.map +1 -0
  14. package/dist/chat/widget-config.d.ts +7 -0
  15. package/dist/chat/widget-config.d.ts.map +1 -0
  16. package/dist/chat/widget-config.js +26 -0
  17. package/dist/chat/widget-config.js.map +1 -0
  18. package/dist/client.d.ts +66 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +49 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/defaults.d.ts +12 -0
  23. package/dist/defaults.d.ts.map +1 -0
  24. package/dist/defaults.js +27 -0
  25. package/dist/defaults.js.map +1 -0
  26. package/dist/embed/loader-types.d.ts +119 -0
  27. package/dist/embed/loader-types.d.ts.map +1 -0
  28. package/dist/embed/loader-types.js +20 -0
  29. package/dist/embed/loader-types.js.map +1 -0
  30. package/dist/embed/loader.d.ts +101 -0
  31. package/dist/embed/loader.d.ts.map +1 -0
  32. package/dist/embed/loader.js +439 -0
  33. package/dist/embed/loader.js.map +1 -0
  34. package/dist/embed/v1.global.js +5 -0
  35. package/dist/embed/v1.global.js.map +1 -0
  36. package/dist/events.d.ts +10 -0
  37. package/dist/events.d.ts.map +1 -0
  38. package/dist/events.js +25 -0
  39. package/dist/events.js.map +1 -0
  40. package/dist/index.d.ts +19 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +29 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/logger.d.ts +14 -0
  45. package/dist/logger.d.ts.map +1 -0
  46. package/dist/logger.js +3 -0
  47. package/dist/logger.js.map +1 -0
  48. package/dist/next/index.d.ts +58 -0
  49. package/dist/next/index.d.ts.map +1 -0
  50. package/dist/next/index.js +83 -0
  51. package/dist/next/index.js.map +1 -0
  52. package/dist/react/index.d.ts +16 -0
  53. package/dist/react/index.d.ts.map +1 -0
  54. package/dist/react/index.js +20 -0
  55. package/dist/react/index.js.map +1 -0
  56. package/dist/react/types.d.ts +27 -0
  57. package/dist/react/types.d.ts.map +1 -0
  58. package/dist/react/types.js +8 -0
  59. package/dist/react/types.js.map +1 -0
  60. package/dist/react/use-chanl.d.ts +27 -0
  61. package/dist/react/use-chanl.d.ts.map +1 -0
  62. package/dist/react/use-chanl.js +57 -0
  63. package/dist/react/use-chanl.js.map +1 -0
  64. package/dist/react/use-chat.d.ts +32 -0
  65. package/dist/react/use-chat.d.ts.map +1 -0
  66. package/dist/react/use-chat.js +224 -0
  67. package/dist/react/use-chat.js.map +1 -0
  68. package/dist/react/use-voice.d.ts +37 -0
  69. package/dist/react/use-voice.d.ts.map +1 -0
  70. package/dist/react/use-voice.js +268 -0
  71. package/dist/react/use-voice.js.map +1 -0
  72. package/dist/react/widget.d.ts +43 -0
  73. package/dist/react/widget.d.ts.map +1 -0
  74. package/dist/react/widget.js +188 -0
  75. package/dist/react/widget.js.map +1 -0
  76. package/dist/storage/session-storage.d.ts +48 -0
  77. package/dist/storage/session-storage.d.ts.map +1 -0
  78. package/dist/storage/session-storage.js +84 -0
  79. package/dist/storage/session-storage.js.map +1 -0
  80. package/dist/types.d.ts +140 -0
  81. package/dist/types.d.ts.map +1 -0
  82. package/dist/types.js +7 -0
  83. package/dist/types.js.map +1 -0
  84. package/dist/voice/audio-recorder.d.ts +43 -0
  85. package/dist/voice/audio-recorder.d.ts.map +1 -0
  86. package/dist/voice/audio-recorder.js +127 -0
  87. package/dist/voice/audio-recorder.js.map +1 -0
  88. package/dist/voice/index.d.ts +13 -0
  89. package/dist/voice/index.d.ts.map +1 -0
  90. package/dist/voice/index.js +16 -0
  91. package/dist/voice/index.js.map +1 -0
  92. package/dist/voice/mock-mode.d.ts +93 -0
  93. package/dist/voice/mock-mode.d.ts.map +1 -0
  94. package/dist/voice/mock-mode.js +375 -0
  95. package/dist/voice/mock-mode.js.map +1 -0
  96. package/dist/voice/transports/index.d.ts +5 -0
  97. package/dist/voice/transports/index.d.ts.map +1 -0
  98. package/dist/voice/transports/index.js +10 -0
  99. package/dist/voice/transports/index.js.map +1 -0
  100. package/dist/voice/transports/transport.d.ts +70 -0
  101. package/dist/voice/transports/transport.d.ts.map +1 -0
  102. package/dist/voice/transports/transport.js +12 -0
  103. package/dist/voice/transports/transport.js.map +1 -0
  104. package/dist/voice/transports/vapi.d.ts +147 -0
  105. package/dist/voice/transports/vapi.d.ts.map +1 -0
  106. package/dist/voice/transports/vapi.js +337 -0
  107. package/dist/voice/transports/vapi.js.map +1 -0
  108. package/dist/voice/transports/webrtc.d.ts +58 -0
  109. package/dist/voice/transports/webrtc.d.ts.map +1 -0
  110. package/dist/voice/transports/webrtc.js +318 -0
  111. package/dist/voice/transports/webrtc.js.map +1 -0
  112. package/dist/voice/transports/websocket.d.ts +39 -0
  113. package/dist/voice/transports/websocket.d.ts.map +1 -0
  114. package/dist/voice/transports/websocket.js +280 -0
  115. package/dist/voice/transports/websocket.js.map +1 -0
  116. package/dist/voice/types.d.ts +323 -0
  117. package/dist/voice/types.d.ts.map +1 -0
  118. package/dist/voice/types.js +41 -0
  119. package/dist/voice/types.js.map +1 -0
  120. package/dist/voice/utils.d.ts +22 -0
  121. package/dist/voice/utils.d.ts.map +1 -0
  122. package/dist/voice/utils.js +44 -0
  123. package/dist/voice/utils.js.map +1 -0
  124. package/dist/voice/voice-client.d.ts +231 -0
  125. package/dist/voice/voice-client.d.ts.map +1 -0
  126. package/dist/voice/voice-client.js +1187 -0
  127. package/dist/voice/voice-client.js.map +1 -0
  128. 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"}