@craftedxp/voice-js 0.4.2 → 0.5.4

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/dist/node.d.ts CHANGED
@@ -1,103 +1,290 @@
1
+ import { RemoteTrack, LocalVideoTrack } from 'livekit-client'
2
+
1
3
  interface ClientTool {
2
- description: string;
3
- parameters: Record<string, unknown>;
4
- usage?: string;
5
- timeoutMs?: number;
6
- example?: string;
7
- handler: (args: Record<string, unknown>) => Promise<string | object> | string | object;
4
+ description: string
5
+ parameters: Record<string, unknown>
6
+ usage?: string
7
+ timeoutMs?: number
8
+ example?: string
9
+ handler: (args: Record<string, unknown>) => Promise<string | object> | string | object
8
10
  }
9
- type ClientToolMap = Record<string, ClientTool>;
11
+ type ClientToolMap = Record<string, ClientTool>
10
12
  interface ClientToolCallFrame {
11
- toolCallId: string;
12
- name: string;
13
- args: Record<string, unknown>;
13
+ toolCallId: string
14
+ name: string
15
+ args: Record<string, unknown>
14
16
  }
15
17
 
16
- type CallState = 'idle' | 'connecting' | 'listening' | 'user_speaking' | 'agent_speaking' | 'ended' | 'error';
17
- type TranscriptEntry = {
18
- id: string;
19
- role: 'user';
20
- text: string;
21
- committed: boolean;
22
- } | {
23
- id: string;
24
- role: 'agent';
25
- text: string;
26
- interrupted?: boolean;
27
- } | {
28
- id: string;
29
- role: 'tool';
30
- text: string;
31
- } | {
32
- id: string;
33
- role: 'system';
34
- text: string;
35
- };
36
- type CallErrorCode = 'missing_credentials' | 'forbidden' | 'mic_denied' | 'mic_start_failed' | 'audio_session_failed' | 'token_expired' | 'token_invalid' | 'unauthorized' | 'network_unreachable' | 'socket_error' | 'payment_required' | 'not_found' | 'silence_timeout' | 'server_error';
18
+ type CallState =
19
+ | 'idle'
20
+ | 'connecting'
21
+ | 'listening'
22
+ | 'user_speaking'
23
+ | 'agent_speaking'
24
+ | 'ended'
25
+ | 'error'
26
+ type TranscriptEntry =
27
+ | {
28
+ id: string
29
+ role: 'user'
30
+ text: string
31
+ committed: boolean
32
+ }
33
+ | {
34
+ id: string
35
+ role: 'agent'
36
+ text: string
37
+ interrupted?: boolean
38
+ }
39
+ | {
40
+ id: string
41
+ role: 'tool'
42
+ text: string
43
+ }
44
+ | {
45
+ id: string
46
+ role: 'system'
47
+ text: string
48
+ }
49
+ type CallErrorCode =
50
+ | 'missing_credentials'
51
+ | 'forbidden'
52
+ | 'mic_denied'
53
+ | 'mic_start_failed'
54
+ | 'audio_session_failed'
55
+ | 'token_expired'
56
+ | 'token_invalid'
57
+ | 'unauthorized'
58
+ | 'network_unreachable'
59
+ | 'socket_error'
60
+ | 'payment_required'
61
+ | 'not_found'
62
+ | 'silence_timeout'
63
+ | 'server_error'
37
64
  interface CallError {
38
- code: CallErrorCode;
39
- message: string;
65
+ code: CallErrorCode
66
+ message: string
40
67
  }
41
- type CallEndReason = 'agent_ended' | 'user_hangup' | 'timeout' | 'error';
68
+ type CallEndReason = 'agent_ended' | 'user_hangup' | 'timeout' | 'error'
42
69
  interface CallEndEvent {
43
- reason: CallEndReason;
44
- errorCode?: CallErrorCode;
45
- durationMs: number;
70
+ reason: CallEndReason
71
+ errorCode?: CallErrorCode
72
+ durationMs: number
46
73
  }
47
74
  interface VolumeEvent {
48
- input: number;
49
- output: number;
75
+ input: number
76
+ output: number
50
77
  }
51
78
  type ServerMessage = Record<string, unknown> & {
52
- type?: string;
53
- };
79
+ type?: string
80
+ }
54
81
  interface ProtocolState {
55
- state: CallState;
56
- transcript: TranscriptEntry[];
57
- agentBubbleId: string | null;
58
- idCounter: number;
59
- endReason: CallEndReason | null;
82
+ state: CallState
83
+ transcript: TranscriptEntry[]
84
+ agentBubbleId: string | null
85
+ idCounter: number
86
+ endReason: CallEndReason | null
60
87
  }
61
- declare const createProtocolState: () => ProtocolState;
88
+ declare const createProtocolState: () => ProtocolState
62
89
  interface ProtocolCallbacks {
63
- onState: (next: CallState) => void;
64
- onTranscript: (entries: TranscriptEntry[]) => void;
65
- onError: (err: CallError) => void;
66
- onInterrupt: () => void;
67
- onAgentTurnStart: (seq?: number) => void;
68
- onAgentTurnEnd: (seq?: number) => void;
69
- onCallEnd: (reason: CallEndReason) => void;
70
- onConnected: () => void;
71
- onClientToolCall: (frame: ClientToolCallFrame) => void;
90
+ onState: (next: CallState) => void
91
+ onTranscript: (entries: TranscriptEntry[]) => void
92
+ onError: (err: CallError) => void
93
+ onInterrupt: () => void
94
+ onAgentTurnStart: (seq?: number) => void
95
+ onAgentTurnEnd: (seq?: number) => void
96
+ onCallEnd: (reason: CallEndReason) => void
97
+ onConnected: () => void
98
+ onClientToolCall: (frame: ClientToolCallFrame) => void
72
99
  }
73
- declare function handleServerMessage(raw: string, state: ProtocolState, cb: ProtocolCallbacks): void;
100
+ declare function handleServerMessage(raw: string, state: ProtocolState, cb: ProtocolCallbacks): void
74
101
  interface BuildWsUrlArgs {
75
- apiBase: string;
76
- agentId: string;
77
- token: string;
78
- bargeIn?: boolean;
102
+ apiBase: string
103
+ agentId: string
104
+ token: string
105
+ bargeIn?: boolean
106
+ }
107
+ declare function buildWsUrl(args: BuildWsUrlArgs): string
108
+
109
+ type SystemMessage =
110
+ | {
111
+ kind: 'room.starting'
112
+ at: string
113
+ }
114
+ | {
115
+ kind: 'room.ending.soon'
116
+ minutesRemaining: 5 | 1
117
+ }
118
+ | {
119
+ kind: 'room.ended'
120
+ reason: 'duration_reached' | 'manual' | 'empty'
121
+ }
122
+ | {
123
+ kind: 'role.promoted'
124
+ participantId: string
125
+ name: string
126
+ }
127
+ | {
128
+ kind: 'role.demoted'
129
+ participantId: string
130
+ name: string
131
+ }
132
+ | {
133
+ kind: 'participant.removed'
134
+ participantId: string
135
+ name: string
136
+ byHost?: string
137
+ }
138
+ | {
139
+ kind: 'notetaker.connected'
140
+ }
141
+ | {
142
+ kind: 'notetaker.disconnected'
143
+ }
144
+ | {
145
+ kind: 'notetaker.partial_degraded'
146
+ participantId: string
147
+ }
148
+ type TranscriptMessage = {
149
+ kind: 'partial'
150
+ participantId: string
151
+ speakerName: string
152
+ text: string
153
+ startedAt: string
154
+ }
155
+
156
+ interface JoinRoomOptions {
157
+ /** Full HTTPS URL of the Voissia server. Same shape as VoiceClientConfig.apiBase. */
158
+ apiBase: string
159
+ /** Server-generated room id (`rm_…`). */
160
+ roomId: string
161
+ /** Shared room join token from the invite link. Mints a fresh participant each call. */
162
+ joinCode: string
163
+ /** Display name the joiner registers under for this participant. */
164
+ name: string
165
+ }
166
+ interface RoomParticipantInfo {
167
+ /** Stable participant id (`p_…`); strips any `guest:` LiveKit identity prefix. */
168
+ participantId: string
169
+ /** Display name as the worker registered it; may be empty. */
170
+ name: string
171
+ }
172
+ type RoomTrackKind = 'audio' | 'video'
173
+ /** What a track is — lets consumers tell a camera apart from a screen share
174
+ * (a participant can publish both at once). Mirrors livekit `Track.Source`. */
175
+ type RoomTrackSource = 'camera' | 'microphone' | 'screen_share' | 'screen_share_audio' | 'unknown'
176
+ interface RoomTrackEvent {
177
+ /** Stable participant id (`p_…`); `guest:` prefix stripped. */
178
+ participantId: string
179
+ kind: RoomTrackKind
180
+ /** Distinguishes camera vs screen_share so each can render as its own tile. */
181
+ source: RoomTrackSource
182
+ /** livekit-client track — call `.attach(el)` / `.detach()` to render. */
183
+ track: RemoteTrack
184
+ }
185
+ type RoomEventName =
186
+ | 'participant.joined'
187
+ | 'participant.left'
188
+ | 'transcript.partial'
189
+ | 'transcript.final'
190
+ | 'system.message'
191
+ | 'room.ended'
192
+ | 'track.subscribed'
193
+ | 'track.unsubscribed'
194
+ | 'active.speakers'
195
+ interface RoomEventPayloads {
196
+ 'participant.joined': RoomParticipantInfo
197
+ 'participant.left': RoomParticipantInfo
198
+ 'transcript.partial': TranscriptMessage
199
+ /**
200
+ * Reserved — the worker currently emits only partials over the transcript
201
+ * topic. Final-utterance events will land in Phase 8 once the worker
202
+ * publishes a `final` kind; the SDK keeps the slot reserved so consumers
203
+ * can register handlers today.
204
+ */
205
+ 'transcript.final': {
206
+ participantId: string
207
+ speakerName: string
208
+ text: string
209
+ startedAt: string
210
+ }
211
+ 'system.message': SystemMessage
212
+ 'room.ended': undefined
213
+ 'track.subscribed': RoomTrackEvent
214
+ 'track.unsubscribed': RoomTrackEvent
215
+ /** participantIds currently speaking (drives an active-speaker UI). */
216
+ 'active.speakers': string[]
217
+ }
218
+ type Handler<E extends RoomEventName> = (payload: RoomEventPayloads[E]) => void
219
+ interface RoomSession {
220
+ /** This session's own stable participant id (`p_…`). Useful to filter
221
+ * yourself out of `active.speakers`, which includes the local participant. */
222
+ readonly participantId: string
223
+ /** Snapshot of the remote participants currently connected. */
224
+ readonly participants: RoomParticipantInfo[]
225
+ /** Subscribe to a typed event. No unsubscribe surface yet (mirrors `Call.onX`). */
226
+ on<E extends RoomEventName>(event: E, handler: Handler<E>): void
227
+ /** Publish the local mic track. Resolves once the track is live on LiveKit. */
228
+ publishMic(): Promise<void>
229
+ /** Publish the local camera track. */
230
+ publishCamera(): Promise<void>
231
+ /** Mid-call mute/unmute of the local mic. */
232
+ setMicEnabled(on: boolean): Promise<void>
233
+ /** Mid-call camera on/off. */
234
+ setCameraEnabled(on: boolean): Promise<void>
235
+ /** Current local mic state (for toggle UI). */
236
+ isMicEnabled(): boolean
237
+ /** Current local camera state (for toggle UI). */
238
+ isCameraEnabled(): boolean
239
+ /** The local camera track for self-view, or null before publishCamera resolves. */
240
+ getLocalCameraTrack(): LocalVideoTrack | null
241
+ /**
242
+ * Remote tracks already subscribed at this moment. A late joiner misses the
243
+ * live `track.subscribed` events for tracks published before it connected
244
+ * (LiveKit delivers them during `connect`, before consumer listeners attach).
245
+ * Call this right after registering `track.subscribed` to backfill them.
246
+ */
247
+ getRemoteTracks(): RoomTrackEvent[]
248
+ /**
249
+ * Start/stop sharing the screen (via `getDisplayMedia`). Pass `{ audio: true }`
250
+ * to also capture shared/system audio where the browser allows it (Chrome:
251
+ * tab or system audio; macOS Chrome is tab-audio only; Safari/Firefox don't
252
+ * capture share audio). Publishes a `screen_share` video track (+ optional
253
+ * `screen_share_audio`); remote peers receive them via `track.subscribed`.
254
+ */
255
+ setScreenShareEnabled(
256
+ on: boolean,
257
+ opts?: {
258
+ audio?: boolean
259
+ },
260
+ ): Promise<void>
261
+ /** Current local screen-share state (for toggle UI). */
262
+ isScreenShareEnabled(): boolean
263
+ /** The local screen-share video track for self-preview, or null when off. */
264
+ getLocalScreenTrack(): LocalVideoTrack | null
265
+ /** Disconnect from LiveKit. Idempotent. Triggers `room.ended` via Disconnected. */
266
+ leave(): Promise<void>
79
267
  }
80
- declare function buildWsUrl(args: BuildWsUrlArgs): string;
81
268
 
82
269
  interface FetchTokenArgs {
83
- /** The agent the SDK is about to call. */
84
- agentId: string;
85
- /**
86
- * Optional consumer-side user identifier. Round-tripped to the server
87
- * as `contactId` for Phase 11 contact memory. The SDK does not
88
- * inspect this; your backend uses it to scope the token mint.
89
- */
90
- userId?: string;
91
- /**
92
- * Per-call structured context lowered into the agent's effective
93
- * system prompt server-side at session open. Opaque to the SDK.
94
- */
95
- context?: Record<string, unknown>;
96
- /**
97
- * String key/value pairs round-tripped on the `call.ended` webhook.
98
- * Capped at 1 KB total server-side. NOT lowered into the system prompt.
99
- */
100
- metadata?: Record<string, string>;
270
+ /** The agent the SDK is about to call. */
271
+ agentId: string
272
+ /**
273
+ * Optional consumer-side user identifier. Round-tripped to the server
274
+ * as `contactId` for Phase 11 contact memory. The SDK does not
275
+ * inspect this; your backend uses it to scope the token mint.
276
+ */
277
+ userId?: string
278
+ /**
279
+ * Per-call structured context lowered into the agent's effective
280
+ * system prompt server-side at session open. Opaque to the SDK.
281
+ */
282
+ context?: Record<string, unknown>
283
+ /**
284
+ * String key/value pairs round-tripped on the `call.ended` webhook.
285
+ * Capped at 1 KB total server-side. NOT lowered into the system prompt.
286
+ */
287
+ metadata?: Record<string, string>
101
288
  }
102
289
  /**
103
290
  * What `fetchToken` may return. The rich object form lets the server
@@ -105,196 +292,209 @@ interface FetchTokenArgs {
105
292
  * compatible — the SDK treats it as `{ token, transport: 'ws' }`.
106
293
  */
107
294
  interface FetchTokenResult {
108
- /** Raw `ct_` to feed into the WS open / WebRTC offer. */
109
- token: string;
110
- /** Server-selected transport. Default `'ws'` if absent. */
111
- transport?: 'ws' | 'webrtc';
112
- /** Required when `transport === 'webrtc'` AND the server uses a
113
- * separate signaling gateway. When omitted on a webrtc result, the
114
- * SDK falls back to the API base's Phase-1 routes (local dev). */
115
- webrtcGatewayBase?: string;
295
+ /** Raw `ct_` to feed into the WS open / WebRTC offer. */
296
+ token: string
297
+ /** Server-selected transport. Default `'ws'` if absent. */
298
+ transport?: 'ws' | 'webrtc'
299
+ /** Required when `transport === 'webrtc'` AND the server uses a
300
+ * separate signaling gateway. When omitted on a webrtc result, the
301
+ * SDK falls back to the API base's Phase-1 routes (local dev). */
302
+ webrtcGatewayBase?: string
116
303
  }
117
- type FetchToken = (args: FetchTokenArgs) => Promise<string | FetchTokenResult>;
304
+ type FetchToken = (args: FetchTokenArgs) => Promise<string | FetchTokenResult>
118
305
  interface VoiceClientConfig {
119
- /**
120
- * Full HTTPS URL of the Voissia server. The WebSocket scheme is
121
- * derived: `https` → `wss`, `http` → `ws`. No trailing slash needed.
122
- */
123
- apiBase: string;
124
- /**
125
- * Called by the SDK whenever it needs a fresh `ct_` token (initial
126
- * connect; mid-call refresh on `token_expired`). Your implementation
127
- * should hit YOUR backend, which holds the `sk_` API key and mints
128
- * via `POST /v1/call-tokens` (or `client.callTokens.mint` from
129
- * @craftedxp/sdk-node). Never embed `sk_` in JS code that ships to a
130
- * client.
131
- */
132
- fetchToken: FetchToken;
133
- /**
134
- * Optional metadata applied to EVERY startCall. Per-call `metadata`
135
- * in `startCall` is merged on top (per-call wins on key conflicts).
136
- * Useful for dashboard-wide tags like `{ surface: 'web', appVersion }`.
137
- */
138
- defaultMetadata?: Record<string, string>;
139
- /**
140
- * Optional context applied to EVERY startCall. Per-call `context` in
141
- * `startCall` is merged on top. Useful for cross-call invariants like
142
- * the signed-in user's locale.
143
- */
144
- defaultContext?: Record<string, unknown>;
306
+ /**
307
+ * Full HTTPS URL of the Voissia server. The WebSocket scheme is
308
+ * derived: `https` → `wss`, `http` → `ws`. No trailing slash needed.
309
+ */
310
+ apiBase: string
311
+ /**
312
+ * Called by the SDK whenever it needs a fresh `ct_` token (initial
313
+ * connect; mid-call refresh on `token_expired`). Your implementation
314
+ * should hit YOUR backend, which holds the `sk_` API key and mints
315
+ * via `POST /v1/call-tokens` (or `client.callTokens.mint` from
316
+ * @craftedxp/sdk-node). Never embed `sk_` in JS code that ships to a
317
+ * client.
318
+ */
319
+ fetchToken: FetchToken
320
+ /**
321
+ * Optional metadata applied to EVERY startCall. Per-call `metadata`
322
+ * in `startCall` is merged on top (per-call wins on key conflicts).
323
+ * Useful for dashboard-wide tags like `{ surface: 'web', appVersion }`.
324
+ */
325
+ defaultMetadata?: Record<string, string>
326
+ /**
327
+ * Optional context applied to EVERY startCall. Per-call `context` in
328
+ * `startCall` is merged on top. Useful for cross-call invariants like
329
+ * the signed-in user's locale.
330
+ */
331
+ defaultContext?: Record<string, unknown>
145
332
  }
146
333
  interface StartCallOptions {
147
- /** The agent to call. */
148
- agentId: string;
149
- /** Per-call user identifier. Round-tripped to fetchToken as `userId`. */
150
- userId?: string;
151
- /**
152
- * Per-call structured context. Merged on top of `defaultContext`
153
- * configured at factory time.
154
- */
155
- context?: Record<string, unknown>;
156
- /**
157
- * Per-call metadata. Merged on top of `defaultMetadata` configured
158
- * at factory time.
159
- */
160
- metadata?: Record<string, string>;
161
- /**
162
- * When false, the SDK + server stay full-duplex but barge-in is
163
- * suppressed. Useful for alarm-style flows where the user shouldn't
164
- * accidentally interrupt the script. Default true.
165
- */
166
- bargeIn?: boolean;
167
- /**
168
- * Client-side tools the agent's LLM can call mid-conversation. Each
169
- * tool's handler runs on the consumer's side; result is fed back to
170
- * the LLM through the existing call WebSocket. Schema and handler
171
- * colocate. Validated synchronously at startCall — bad input throws.
172
- *
173
- * See docs/integration-echocheck.md for the wire protocol and the
174
- * server-side guarantees.
175
- */
176
- clientTools?: ClientToolMap;
177
- /**
178
- * Test-only escape hatch — pass a pre-minted `ct_` directly and skip
179
- * the `fetchToken` call. Don't use this in production code: tokens
180
- * expire and the SDK can't re-mint without the callback.
181
- */
182
- token?: string;
183
- onStateChange?: (state: CallState) => void;
184
- onTranscript?: (entries: TranscriptEntry[]) => void;
185
- onError?: (err: CallError) => void;
186
- onEnd?: (end: CallEndEvent) => void;
187
- /** Volume-meter event for VU UIs. ~10 Hz cadence (browser bundle only). */
188
- onVolume?: (vol: VolumeEvent) => void;
189
- /**
190
- * Fires when the server signals barge-in (the user started talking
191
- * mid-agent-turn). The browser bundle automatically flushes its
192
- * built-in audio playback before this callback runs; the callback is
193
- * fired regardless. Node / Electron consumers with custom playback
194
- * should drain their audio queue here so the agent goes silent
195
- * immediately.
196
- */
197
- onInterrupt?: () => void;
198
- /**
199
- * Fires on `agent_turn_start` — the server has begun a new agent
200
- * turn. The state-machine transition to `agent_speaking` happens at
201
- * the same moment via `onStateChange`; use this when you want a
202
- * precise turn anchor (e.g. "agent has been speaking for N ms" UIs)
203
- * without diffing state.
204
- */
205
- onAgentTurnStart?: () => void;
334
+ /** The agent to call. */
335
+ agentId: string
336
+ /** Per-call user identifier. Round-tripped to fetchToken as `userId`. */
337
+ userId?: string
338
+ /**
339
+ * Per-call structured context. Merged on top of `defaultContext`
340
+ * configured at factory time.
341
+ */
342
+ context?: Record<string, unknown>
343
+ /**
344
+ * Per-call metadata. Merged on top of `defaultMetadata` configured
345
+ * at factory time.
346
+ */
347
+ metadata?: Record<string, string>
348
+ /**
349
+ * When false, the SDK + server stay full-duplex but barge-in is
350
+ * suppressed. Useful for alarm-style flows where the user shouldn't
351
+ * accidentally interrupt the script. Default true.
352
+ */
353
+ bargeIn?: boolean
354
+ /**
355
+ * Client-side tools the agent's LLM can call mid-conversation. Each
356
+ * tool's handler runs on the consumer's side; result is fed back to
357
+ * the LLM through the existing call WebSocket. Schema and handler
358
+ * colocate. Validated synchronously at startCall — bad input throws.
359
+ *
360
+ * See docs/integration-echocheck.md for the wire protocol and the
361
+ * server-side guarantees.
362
+ */
363
+ clientTools?: ClientToolMap
364
+ /**
365
+ * Test-only escape hatch — pass a pre-minted `ct_` directly and skip
366
+ * the `fetchToken` call. Don't use this in production code: tokens
367
+ * expire and the SDK can't re-mint without the callback.
368
+ */
369
+ token?: string
370
+ onStateChange?: (state: CallState) => void
371
+ onTranscript?: (entries: TranscriptEntry[]) => void
372
+ onError?: (err: CallError) => void
373
+ onEnd?: (end: CallEndEvent) => void
374
+ /** Volume-meter event for VU UIs. ~10 Hz cadence (browser bundle only). */
375
+ onVolume?: (vol: VolumeEvent) => void
376
+ /**
377
+ * Fires when the server signals barge-in (the user started talking
378
+ * mid-agent-turn). The browser bundle automatically flushes its
379
+ * built-in audio playback before this callback runs; the callback is
380
+ * fired regardless. Node / Electron consumers with custom playback
381
+ * should drain their audio queue here so the agent goes silent
382
+ * immediately.
383
+ */
384
+ onInterrupt?: () => void
385
+ /**
386
+ * Fires on `agent_turn_start` — the server has begun a new agent
387
+ * turn. The state-machine transition to `agent_speaking` happens at
388
+ * the same moment via `onStateChange`; use this when you want a
389
+ * precise turn anchor (e.g. "agent has been speaking for N ms" UIs)
390
+ * without diffing state.
391
+ */
392
+ onAgentTurnStart?: () => void
206
393
  }
207
394
  interface Call {
208
- /** Current state. Snapshot — subscribe via onStateChange for live updates. */
209
- readonly state: CallState;
210
- /** Full transcript so far. Snapshot — subscribe via onTranscript for live updates. */
211
- readonly transcript: TranscriptEntry[];
212
- /** True after `mute()` and before `unmute()`. */
213
- readonly isMuted: boolean;
214
- /** End the call locally. Closes the WS, stops the mic, fires onEnd. Idempotent. */
215
- end: () => void;
216
- /** Mute mic frames. Wire stays active so server endpointing doesn't false-positive. Idempotent. */
217
- mute: () => void;
218
- /** Unmute mic frames. Idempotent. */
219
- unmute: () => void;
395
+ /** Current state. Snapshot — subscribe via onStateChange for live updates. */
396
+ readonly state: CallState
397
+ /** Full transcript so far. Snapshot — subscribe via onTranscript for live updates. */
398
+ readonly transcript: TranscriptEntry[]
399
+ /** True after `mute()` and before `unmute()`. */
400
+ readonly isMuted: boolean
401
+ /** End the call locally. Closes the WS, stops the mic, fires onEnd. Idempotent. */
402
+ end: () => void
403
+ /** Mute mic frames. Wire stays active so server endpointing doesn't false-positive. Idempotent. */
404
+ mute: () => void
405
+ /** Unmute mic frames. Idempotent. */
406
+ unmute: () => void
220
407
  }
221
408
  interface VoiceClientFactory {
222
- /** Read back the resolved config (post trailing-slash normalisation). */
223
- readonly config: VoiceClientConfig;
224
- /**
225
- * Open a fresh call. Returns when the WS is open; rejects on
226
- * pre-flight failure (missing config, fetchToken throw, etc). Mid-
227
- * call failures arrive via the per-call `onError` callback — they
228
- * don't reject this promise.
229
- */
230
- startCall: (options: StartCallOptions) => Promise<Call>;
409
+ /** Read back the resolved config (post trailing-slash normalisation). */
410
+ readonly config: VoiceClientConfig
411
+ /**
412
+ * Open a fresh call. Returns when the WS is open; rejects on
413
+ * pre-flight failure (missing config, fetchToken throw, etc). Mid-
414
+ * call failures arrive via the per-call `onError` callback — they
415
+ * don't reject this promise.
416
+ */
417
+ startCall: (options: StartCallOptions) => Promise<Call>
418
+ /**
419
+ * Phase 7 (multi-party rooms). Browser only. Exchange a single-use
420
+ * joinCode for a LiveKit JWT and connect to the room. The returned
421
+ * `RoomSession` exposes a typed event surface
422
+ * (participant.joined / participant.left / transcript.partial /
423
+ * transcript.final / system.message / room.ended) plus
424
+ * publishMic / publishCamera / leave. The Node bundle does NOT
425
+ * implement this — livekit-client is a browser-only WebRTC client.
426
+ */
427
+ joinRoom?: (options: Omit<JoinRoomOptions, 'apiBase'>) => Promise<RoomSession>
231
428
  }
232
429
 
233
- type RWSEvent = {
234
- type: 'open';
235
- } | {
236
- type: 'reconnected';
237
- } | {
238
- type: 'message';
239
- data: string | ArrayBuffer;
240
- } | {
241
- type: 'close';
242
- code: number;
243
- reason: string;
244
- permanent: boolean;
245
- } | {
246
- type: 'error';
247
- error: Error;
248
- };
430
+ type RWSEvent =
431
+ | {
432
+ type: 'open'
433
+ }
434
+ | {
435
+ type: 'reconnected'
436
+ }
437
+ | {
438
+ type: 'message'
439
+ data: string | ArrayBuffer
440
+ }
441
+ | {
442
+ type: 'close'
443
+ code: number
444
+ reason: string
445
+ permanent: boolean
446
+ }
447
+ | {
448
+ type: 'error'
449
+ error: Error
450
+ }
249
451
  interface WebSocketLike {
250
- binaryType: string;
251
- readyState: number;
252
- onopen: ((ev: unknown) => void) | null;
253
- onmessage: ((ev: {
254
- data: string | ArrayBuffer;
255
- }) => void) | null;
256
- onerror: ((ev: unknown) => void) | null;
257
- onclose: ((ev: {
258
- code: number;
259
- reason: string;
260
- }) => void) | null;
261
- send: (data: string | ArrayBuffer | ArrayBufferView) => void;
262
- close: (code?: number, reason?: string) => void;
452
+ binaryType: string
453
+ readyState: number
454
+ onopen: ((ev: unknown) => void) | null
455
+ onmessage: ((ev: { data: string | ArrayBuffer }) => void) | null
456
+ onerror: ((ev: unknown) => void) | null
457
+ onclose: ((ev: { code: number; reason: string }) => void) | null
458
+ send: (data: string | ArrayBuffer | ArrayBufferView) => void
459
+ close: (code?: number, reason?: string) => void
263
460
  }
264
- type WebSocketFactory = (url: string) => WebSocketLike;
461
+ type WebSocketFactory = (url: string) => WebSocketLike
265
462
  interface RWSOptions {
266
- url: string;
267
- wsFactory: WebSocketFactory;
268
- maxRetries?: number;
269
- initialBackoffMs?: number;
270
- maxBackoffMs?: number;
463
+ url: string
464
+ wsFactory: WebSocketFactory
465
+ maxRetries?: number
466
+ initialBackoffMs?: number
467
+ maxBackoffMs?: number
468
+ }
469
+ declare const createReconnectingWebSocket: (
470
+ options: RWSOptions,
471
+ onEvent: (ev: RWSEvent) => void,
472
+ ) => {
473
+ send: (data: string | ArrayBuffer | ArrayBufferView) => void
474
+ close: (code?: number, reason?: string) => void
475
+ readyState: () => number
271
476
  }
272
- declare const createReconnectingWebSocket: (options: RWSOptions, onEvent: (ev: RWSEvent) => void) => {
273
- send: (data: string | ArrayBuffer | ArrayBufferView) => void;
274
- close: (code?: number, reason?: string) => void;
275
- readyState: () => number;
276
- };
277
- type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>;
477
+ type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>
278
478
 
279
479
  interface NodeStartCallOptions extends StartCallOptions {
280
- /**
281
- * Fires for each binary PCM frame the server pushes (Int16 LE mono
282
- * @ 16 kHz — same as the browser playback path). Wire to your
283
- * preferred output: write to a `sox -t raw -r 16000 -e signed -b 16
284
- * -c 1 - default` subprocess, queue into PortAudio, relay over RTP,
285
- * etc. If you don't supply this callback, agent audio is dropped on
286
- * the floor.
287
- */
288
- onAudioChunk?: (pcm: ArrayBuffer) => void;
480
+ /**
481
+ * Fires for each binary PCM frame the server pushes (Int16 LE mono
482
+ * @ 16 kHz — same as the browser playback path). Wire to your
483
+ * preferred output: write to a `sox -t raw -r 16000 -e signed -b 16
484
+ * -c 1 - default` subprocess, queue into PortAudio, relay over RTP,
485
+ * etc. If you don't supply this callback, agent audio is dropped on
486
+ * the floor.
487
+ */
488
+ onAudioChunk?: (pcm: ArrayBuffer) => void
289
489
  }
290
490
  interface NodeCall extends Call {
291
- /**
292
- * Push one mic frame to the server. Expected: Int16 LE mono PCM @
293
- * 16 kHz. Capture cadence ~100 ms / ~3.2 KB per frame is fine.
294
- * Returns `false` if the WS isn't open yet (caller may want to
295
- * back-pressure or drop).
296
- */
297
- sendAudioChunk: (pcm: ArrayBuffer | ArrayBufferView) => boolean;
491
+ /**
492
+ * Push one mic frame to the server. Expected: Int16 LE mono PCM @
493
+ * 16 kHz. Capture cadence ~100 ms / ~3.2 KB per frame is fine.
494
+ * Returns `false` if the WS isn't open yet (caller may want to
495
+ * back-pressure or drop).
496
+ */
497
+ sendAudioChunk: (pcm: ArrayBuffer | ArrayBufferView) => boolean
298
498
  }
299
499
  /**
300
500
  * Node bundle's analog of `VoiceClientFactory`. Same shape but
@@ -304,10 +504,35 @@ interface NodeCall extends Call {
304
504
  * entry. Browser entry returns the base `VoiceClientFactory` type.
305
505
  */
306
506
  interface NodeVoiceClientFactory {
307
- readonly config: VoiceClientConfig;
308
- startCall: (options: NodeStartCallOptions) => Promise<NodeCall>;
507
+ readonly config: VoiceClientConfig
508
+ startCall: (options: NodeStartCallOptions) => Promise<NodeCall>
309
509
  }
310
510
 
511
+ /**
512
+ * Canonical payload a tenant places in their VoIP/FCM push so an
513
+ * agent-initiated call can connect. It is the `callTokens.mint` result
514
+ * (token + transport) plus two optional display fields for the native
515
+ * incoming-call UI. The host receives the push, runs `parseIncomingCall`,
516
+ * and on accept passes `token` (+ transport / webrtcGatewayBase) into the
517
+ * voice client. Web background-wake is best-effort (Web Push); native is
518
+ * the real target. See docs/sdks.md "Agent-initiated calls".
519
+ */
520
+ interface IncomingCallPayload {
521
+ token: string
522
+ agentId: string
523
+ transport: 'ws' | 'webrtc'
524
+ webrtcGatewayBase?: string
525
+ expiresAt?: number
526
+ agentName?: string
527
+ agentAvatarUrl?: string
528
+ }
529
+ /**
530
+ * Validate + normalise a raw push payload into an IncomingCallPayload.
531
+ * Throws synchronously on malformed input. Unknown transports fall back to
532
+ * 'ws'; webrtcGatewayBase is ignored unless transport === 'webrtc'.
533
+ */
534
+ declare const parseIncomingCall: (raw: unknown) => IncomingCallPayload
535
+
311
536
  /**
312
537
  * One-time SDK setup for Node.js / Electron-main consumers. Returns a
313
538
  * factory you call `startCall` on for every voice call. Same shape as
@@ -336,6 +561,41 @@ interface NodeVoiceClientFactory {
336
561
  *
337
562
  * mic.stdout.on('data', (chunk) => call.sendAudioChunk(chunk))
338
563
  */
339
- declare function configureVoiceClient(config: VoiceClientConfig): NodeVoiceClientFactory;
564
+ declare function configureVoiceClient(config: VoiceClientConfig): NodeVoiceClientFactory
340
565
 
341
- export { type Call, type CallEndEvent, type CallEndReason, type CallError, type CallErrorCode, type CallState, type ClientTool, type ClientToolMap, type FetchToken, type FetchTokenArgs, type FetchTokenResult, type NodeCall, type NodeStartCallOptions, type NodeVoiceClientFactory, type ProtocolCallbacks, type ProtocolState, type RWSEvent, type RWSOptions, type ReconnectingWebSocket, type ServerMessage, type StartCallOptions, type TranscriptEntry, type VoiceClientConfig, type VoiceClientFactory, type VolumeEvent, type WebSocketFactory, type WebSocketLike, buildWsUrl, configureVoiceClient, createProtocolState, createReconnectingWebSocket, handleServerMessage };
566
+ export {
567
+ type Call,
568
+ type CallEndEvent,
569
+ type CallEndReason,
570
+ type CallError,
571
+ type CallErrorCode,
572
+ type CallState,
573
+ type ClientTool,
574
+ type ClientToolMap,
575
+ type FetchToken,
576
+ type FetchTokenArgs,
577
+ type FetchTokenResult,
578
+ type IncomingCallPayload,
579
+ type NodeCall,
580
+ type NodeStartCallOptions,
581
+ type NodeVoiceClientFactory,
582
+ type ProtocolCallbacks,
583
+ type ProtocolState,
584
+ type RWSEvent,
585
+ type RWSOptions,
586
+ type ReconnectingWebSocket,
587
+ type ServerMessage,
588
+ type StartCallOptions,
589
+ type TranscriptEntry,
590
+ type VoiceClientConfig,
591
+ type VoiceClientFactory,
592
+ type VolumeEvent,
593
+ type WebSocketFactory,
594
+ type WebSocketLike,
595
+ buildWsUrl,
596
+ configureVoiceClient,
597
+ createProtocolState,
598
+ createReconnectingWebSocket,
599
+ handleServerMessage,
600
+ parseIncomingCall,
601
+ }