@craftedxp/voice-js 0.4.1 → 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/CONSUMING.md +10 -2
- package/README.md +72 -11
- package/dist/browser.d.mts +178 -1
- package/dist/browser.d.ts +208 -0
- package/dist/browser.js +244 -33
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +252 -38
- package/dist/browser.mjs.map +1 -1
- package/dist/embed.iife.js +22677 -452
- package/dist/node.d.mts +177 -1
- package/dist/node.d.ts +199 -0
- package/dist/node.js +30 -0
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +30 -1
- package/dist/node.mjs.map +1 -1
- package/package.json +4 -1
package/CONSUMING.md
CHANGED
|
@@ -79,14 +79,22 @@ export async function POST(req) {
|
|
|
79
79
|
|
|
80
80
|
// Mint with the user's identity. Server attaches contactId, applies the
|
|
81
81
|
// org's tier gate, etc.
|
|
82
|
-
const
|
|
82
|
+
const result = await platform.callTokens.mint({
|
|
83
83
|
agentId,
|
|
84
84
|
contactId: session.userId,
|
|
85
85
|
context,
|
|
86
86
|
metadata,
|
|
87
87
|
ttlSeconds: 600,
|
|
88
88
|
})
|
|
89
|
-
|
|
89
|
+
// Forward transport + webrtcGatewayBase so the browser SDK can
|
|
90
|
+
// dispatch WS vs WebRTC per the agent's configuration. New agents
|
|
91
|
+
// default to WebRTC since 2026-05-16; legacy agents keep their
|
|
92
|
+
// stored 'ws' setting.
|
|
93
|
+
return Response.json({
|
|
94
|
+
token: result.token,
|
|
95
|
+
transport: result.transport,
|
|
96
|
+
webrtcGatewayBase: result.webrtcGatewayBase,
|
|
97
|
+
})
|
|
90
98
|
}
|
|
91
99
|
```
|
|
92
100
|
|
package/README.md
CHANGED
|
@@ -41,13 +41,22 @@ import { configureVoiceClient } from '@craftedxp/voice-js'
|
|
|
41
41
|
const voice = configureVoiceClient({
|
|
42
42
|
apiBase: 'https://api.your-server.com',
|
|
43
43
|
// SDK calls this whenever it needs a fresh ct_ — initial connect
|
|
44
|
-
// and any mid-call token refresh. Your backend handles the mint
|
|
44
|
+
// and any mid-call token refresh. Your backend handles the mint and
|
|
45
|
+
// forwards the mint response's `transport` + `webrtcGatewayBase` so
|
|
46
|
+
// the SDK can dispatch WS vs WebRTC per the agent's configuration.
|
|
47
|
+
// (New agents default to WebRTC since 2026-05-16. The bare-string
|
|
48
|
+
// form below is still accepted for back-compat — it always uses WS.)
|
|
45
49
|
fetchToken: async ({ agentId }) => {
|
|
46
50
|
const r = await fetch('/api/voice/mint', {
|
|
47
51
|
method: 'POST',
|
|
48
52
|
body: JSON.stringify({ agentId }),
|
|
49
53
|
})
|
|
50
|
-
|
|
54
|
+
const body = await r.json()
|
|
55
|
+
return {
|
|
56
|
+
token: body.token,
|
|
57
|
+
transport: body.transport, // 'ws' | 'webrtc'
|
|
58
|
+
webrtcGatewayBase: body.webrtcGatewayBase, // present when transport=webrtc
|
|
59
|
+
}
|
|
51
60
|
},
|
|
52
61
|
// Optional — applied to every call. Per-call options merge on top.
|
|
53
62
|
defaultMetadata: { surface: 'web', appVersion: '1.4.0' },
|
|
@@ -131,12 +140,12 @@ The Node bundle has the same `configureVoiceClient` / `startCall` shape, plus an
|
|
|
131
140
|
|
|
132
141
|
### `configureVoiceClient(config)`
|
|
133
142
|
|
|
134
|
-
| Field | Type
|
|
135
|
-
| ----------------- |
|
|
136
|
-
| `apiBase` | `string`
|
|
137
|
-
| `fetchToken` | `(args) => Promise<string>` | Called by the SDK whenever it needs a fresh `ct_`.
|
|
138
|
-
| `defaultMetadata` | `Record<string, string>?`
|
|
139
|
-
| `defaultContext` | `Record<string, unknown>?`
|
|
143
|
+
| Field | Type | Notes |
|
|
144
|
+
| ----------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
+
| `apiBase` | `string` | Full HTTPS URL of the Voissia server. WS scheme derived: `https`→`wss`. Trailing slash optional. |
|
|
146
|
+
| `fetchToken` | `(args) => Promise<string \| FetchTokenResult>` | Called by the SDK whenever it needs a fresh `ct_`. Args: `{ agentId, userId?, context?, metadata? }`. Return either a bare `ct_` string (always WS, back-compat) or the rich `{ token, transport, webrtcGatewayBase? }` object so the SDK can dispatch WS vs WebRTC per the agent's configuration. |
|
|
147
|
+
| `defaultMetadata` | `Record<string, string>?` | Applied to every `startCall`. Per-call merges on top. |
|
|
148
|
+
| `defaultContext` | `Record<string, unknown>?` | Applied to every `startCall`. Per-call merges on top. |
|
|
140
149
|
|
|
141
150
|
Returns a `VoiceClientFactory` with one method:
|
|
142
151
|
|
|
@@ -211,6 +220,50 @@ type CallErrorCode =
|
|
|
211
220
|
type CallEndReason = 'agent_ended' | 'user_hangup' | 'timeout' | 'error'
|
|
212
221
|
```
|
|
213
222
|
|
|
223
|
+
## Animating while the agent is talking
|
|
224
|
+
|
|
225
|
+
Four optional callbacks on `startCall` give you everything you need to drive an "agent is talking" animation:
|
|
226
|
+
|
|
227
|
+
| Callback | What |
|
|
228
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
229
|
+
| `onStateChange(state)` | `state === 'agent_speaking'` is true while the agent is in its speaking turn. |
|
|
230
|
+
| `onAgentTurnStart()` | Fires the moment the agent starts a turn — a discrete trigger if you want an "activation" cue. |
|
|
231
|
+
| `onInterrupt()` | The user barged in. End the animation early. |
|
|
232
|
+
| `onVolume({ input, output })` | Real-time amplitude. `output` is the agent's playback level (≈ what the listener hears); `input` is your mic. |
|
|
233
|
+
|
|
234
|
+
`onStateChange === 'agent_speaking'` is **protocol-driven**: it flips on as soon as the server begins the turn, _before_ the first audio sample reaches the speaker. For a visual that follows what the listener actually hears, drive it from `onVolume.output`.
|
|
235
|
+
|
|
236
|
+
The usual recipe is a stable gate × live amplitude:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
let isAgentTurn = false
|
|
240
|
+
let amplitude = 0
|
|
241
|
+
|
|
242
|
+
const call = await client.startCall({
|
|
243
|
+
agentId,
|
|
244
|
+
onStateChange: (s) => {
|
|
245
|
+
isAgentTurn = s === 'agent_speaking'
|
|
246
|
+
render()
|
|
247
|
+
},
|
|
248
|
+
onInterrupt: () => {
|
|
249
|
+
isAgentTurn = false
|
|
250
|
+
render()
|
|
251
|
+
},
|
|
252
|
+
onVolume: ({ output }) => {
|
|
253
|
+
amplitude = output
|
|
254
|
+
render()
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
function render() {
|
|
259
|
+
if (isAgentTurn)
|
|
260
|
+
showPulse({ scale: 1 + amplitude }) // your renderer
|
|
261
|
+
else hidePulse()
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
> 1:1 calls only — in multi-party rooms (`joinRoom`) the in-room agent is a silent notetaker and has no speaking turn, so none of these signals fire for it.
|
|
266
|
+
|
|
214
267
|
## Client tools
|
|
215
268
|
|
|
216
269
|
You can declare tools the agent's LLM can call **on the consumer's machine**. The
|
|
@@ -251,7 +304,7 @@ sent back; throws become `{ error: ... }` frames. The server enforces a default
|
|
|
251
304
|
10s / max 30s timeout per `timeoutMs` in your declaration.
|
|
252
305
|
|
|
253
306
|
For the full wire protocol, sequencing, and constraints see
|
|
254
|
-
[`docs/
|
|
307
|
+
[`docs/sdks.md` → Client tools](../../docs/sdks.md#client-tools).
|
|
255
308
|
|
|
256
309
|
## Migrating from `@voxline/web`
|
|
257
310
|
|
|
@@ -324,7 +377,7 @@ false-positive VAD on background noise). Three quick fixes, in order:
|
|
|
324
377
|
|
|
325
378
|
For the full diagnostic walkthrough (including the rarer Gemini-Live
|
|
326
379
|
stale-audio-leak case and audio-handling guidance for Node/Electron consumers),
|
|
327
|
-
see [`docs/
|
|
380
|
+
see [`docs/sdks.md` → Audio quality troubleshooting](../../docs/sdks.md#audio-quality-troubleshooting).
|
|
328
381
|
|
|
329
382
|
## Embed widget
|
|
330
383
|
|
|
@@ -344,7 +397,15 @@ Renders a floating call button with a Shadow-DOM transcript panel. Pre-mint the
|
|
|
344
397
|
|
|
345
398
|
## Status
|
|
346
399
|
|
|
347
|
-
- **0.
|
|
400
|
+
- **0.5.4** (current) — Screen sharing. `setScreenShareEnabled(on, { audio })` publishes a `screen_share` video track (and, where the browser allows, a `screen_share_audio` track — Chrome captures tab/system audio; macOS Chrome is tab-audio only; Safari/Firefox don't capture share audio). `RoomTrackEvent` gains a `source` field (`camera` / `microphone` / `screen_share` / `screen_share_audio` / `unknown`) so a screen share can render as its own tile instead of replacing the participant's camera. Adds `isScreenShareEnabled()` and `getLocalScreenTrack()`. No 1:1 call-surface change. Drop-in for 0.5.3 consumers (the new `source` field is additive).
|
|
401
|
+
- 0.5.3 — `session.participantId` (this session's own stable `p_…` id). `active.speakers` includes the local participant, so a focus-tile UI rendered you as a remote when you spoke (no remote track → blank tile with the raw id). Filter `participantId` out of `active.speakers` to focus only remote speakers (self-view when none). No 1:1 call-surface change. Drop-in upgrade for 0.5.2 consumers.
|
|
402
|
+
- 0.5.2 — `getRemoteTracks(): RoomTrackEvent[]`. Returns remote tracks already subscribed at call time. A late joiner misses the live `track.subscribed` events for tracks published before it connected (LiveKit delivers them during `connect`, before consumer listeners attach), so it never rendered participants who already had their camera on (e.g. the host). Call `getRemoteTracks()` right after registering `track.subscribed` to backfill them. No 1:1 call-surface change. Drop-in upgrade for 0.5.1 consumers.
|
|
403
|
+
- 0.5.1 — Room video surface. `RoomSession` gains: `track.subscribed` / `track.unsubscribed` events with payload `{ participantId: string; kind: 'audio' | 'video'; track: RemoteTrack }` (raw livekit-client track — call `track.attach(el)` / `track.detach()`); `active.speakers` event (`string[]` of participantIds currently speaking, drives active-speaker UI); `setMicEnabled(on: boolean): Promise<void>` / `setCameraEnabled(on: boolean): Promise<void>` (mid-call toggles); `isMicEnabled(): boolean` / `isCameraEnabled(): boolean` (read current state for toggle button UI); `getLocalCameraTrack(): LocalVideoTrack | null` (attach to self-view element). No API changes to the 1:1 call surface (`startCall` / `Call`). Drop-in upgrade for 0.5.0 consumers.
|
|
404
|
+
- 0.5.0 — Multi-party rooms. `joinRoom({ roomId, joinCode, name })` returns a typed `RoomSession` with `participant.{joined,left}`, `transcript.partial`, `system.message`, `room.ended` events. Adds `livekit-client` as a direct dependency (~250 KB gzipped, tree-shakes if unused). `publishMic()` / `publishCamera()` / `leave()` methods. See [`docs/sdks.md` → Multi-party rooms](../../docs/sdks.md#multi-party-rooms).
|
|
405
|
+
- 0.4.2 — WebRTC reliability: `onicecandidate` + `onconnectionstatechange` listeners now register before `setLocalDescription` (where ICE gathering starts). Candidates emitted before `callId` is known are buffered and flushed once the answer arrives. Browsers buffer candidates internally so the prior listen-after-`setRemoteDescription` pattern worked in Chrome/Safari, but the explicit ordering is correct per spec and matches the fix shipped in `@craftedxp/voice-rn@0.4.1`. No API surface change — drop-in upgrade.
|
|
406
|
+
- 0.4.1 — `client_tools` over the WebRTC DataChannel — tool register on `connected`, dispatch on `client_tool_call` frames, parity with the WS transport.
|
|
407
|
+
- 0.4.0 — WebRTC transport support. `fetchToken` may now return `{ token, transport, webrtcGatewayBase? }`; the SDK dispatches WS vs WebRTC accordingly. Backwards-compatible — bare-string returns still always use WS.
|
|
408
|
+
- 0.3.2 — bug fix: `onStateChange` now fires for state transitions driven by server frames (`connected → listening`, `agent_turn_start → agent_speaking`, etc.). Latent regression since 0.2.0; `onTranscript`-only consumers were unaffected, but anyone deriving UI from `onStateChange` should upgrade. No API changes — drop-in.
|
|
348
409
|
- 0.3.1 — adds `onInterrupt` / `onAgentTurnStart` callbacks on `StartCallOptions` and `NodeVoiceClientFactory` proper return type for the Node entry. Backwards-compatible. **Use 0.3.2 instead** — both new callbacks depend on the state-callback path that 0.3.2 fixes.
|
|
349
410
|
- 0.3.0 — adds client-tools support. New `clientTools` option on `startCall` accepts a `ClientToolMap` (description, parameters, handler, optional usage/timeoutMs/example). Browser and Node bundles both supported. Backwards-compatible — existing consumers see no change.
|
|
350
411
|
- 0.2.0 — first `@craftedxp/voice-js` release. Browser + Node dual bundle, `fetchToken` factory, voice-rn 0.3.x parity. Migration path from `@voxline/web@0.1.0` documented above.
|
package/dist/browser.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RemoteTrack, LocalVideoTrack } from 'livekit-client';
|
|
2
|
+
|
|
1
3
|
interface ClientTool {
|
|
2
4
|
description: string;
|
|
3
5
|
parameters: Record<string, unknown>;
|
|
@@ -79,6 +81,146 @@ interface BuildWsUrlArgs {
|
|
|
79
81
|
}
|
|
80
82
|
declare function buildWsUrl(args: BuildWsUrlArgs): string;
|
|
81
83
|
|
|
84
|
+
type SystemMessage = {
|
|
85
|
+
kind: 'room.starting';
|
|
86
|
+
at: string;
|
|
87
|
+
} | {
|
|
88
|
+
kind: 'room.ending.soon';
|
|
89
|
+
minutesRemaining: 5 | 1;
|
|
90
|
+
} | {
|
|
91
|
+
kind: 'room.ended';
|
|
92
|
+
reason: 'duration_reached' | 'manual' | 'empty';
|
|
93
|
+
} | {
|
|
94
|
+
kind: 'role.promoted';
|
|
95
|
+
participantId: string;
|
|
96
|
+
name: string;
|
|
97
|
+
} | {
|
|
98
|
+
kind: 'role.demoted';
|
|
99
|
+
participantId: string;
|
|
100
|
+
name: string;
|
|
101
|
+
} | {
|
|
102
|
+
kind: 'participant.removed';
|
|
103
|
+
participantId: string;
|
|
104
|
+
name: string;
|
|
105
|
+
byHost?: string;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'notetaker.connected';
|
|
108
|
+
} | {
|
|
109
|
+
kind: 'notetaker.disconnected';
|
|
110
|
+
} | {
|
|
111
|
+
kind: 'notetaker.partial_degraded';
|
|
112
|
+
participantId: string;
|
|
113
|
+
};
|
|
114
|
+
type TranscriptMessage = {
|
|
115
|
+
kind: 'partial';
|
|
116
|
+
participantId: string;
|
|
117
|
+
speakerName: string;
|
|
118
|
+
text: string;
|
|
119
|
+
startedAt: string;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
interface JoinRoomOptions {
|
|
123
|
+
/** Full HTTPS URL of the Voissia server. Same shape as VoiceClientConfig.apiBase. */
|
|
124
|
+
apiBase: string;
|
|
125
|
+
/** Server-generated room id (`rm_…`). */
|
|
126
|
+
roomId: string;
|
|
127
|
+
/** Shared room join token from the invite link. Mints a fresh participant each call. */
|
|
128
|
+
joinCode: string;
|
|
129
|
+
/** Display name the joiner registers under for this participant. */
|
|
130
|
+
name: string;
|
|
131
|
+
}
|
|
132
|
+
interface RoomParticipantInfo {
|
|
133
|
+
/** Stable participant id (`p_…`); strips any `guest:` LiveKit identity prefix. */
|
|
134
|
+
participantId: string;
|
|
135
|
+
/** Display name as the worker registered it; may be empty. */
|
|
136
|
+
name: string;
|
|
137
|
+
}
|
|
138
|
+
type RoomTrackKind = 'audio' | 'video';
|
|
139
|
+
/** What a track is — lets consumers tell a camera apart from a screen share
|
|
140
|
+
* (a participant can publish both at once). Mirrors livekit `Track.Source`. */
|
|
141
|
+
type RoomTrackSource = 'camera' | 'microphone' | 'screen_share' | 'screen_share_audio' | 'unknown';
|
|
142
|
+
interface RoomTrackEvent {
|
|
143
|
+
/** Stable participant id (`p_…`); `guest:` prefix stripped. */
|
|
144
|
+
participantId: string;
|
|
145
|
+
kind: RoomTrackKind;
|
|
146
|
+
/** Distinguishes camera vs screen_share so each can render as its own tile. */
|
|
147
|
+
source: RoomTrackSource;
|
|
148
|
+
/** livekit-client track — call `.attach(el)` / `.detach()` to render. */
|
|
149
|
+
track: RemoteTrack;
|
|
150
|
+
}
|
|
151
|
+
type RoomEventName = 'participant.joined' | 'participant.left' | 'transcript.partial' | 'transcript.final' | 'system.message' | 'room.ended' | 'track.subscribed' | 'track.unsubscribed' | 'active.speakers';
|
|
152
|
+
interface RoomEventPayloads {
|
|
153
|
+
'participant.joined': RoomParticipantInfo;
|
|
154
|
+
'participant.left': RoomParticipantInfo;
|
|
155
|
+
'transcript.partial': TranscriptMessage;
|
|
156
|
+
/**
|
|
157
|
+
* Reserved — the worker currently emits only partials over the transcript
|
|
158
|
+
* topic. Final-utterance events will land in Phase 8 once the worker
|
|
159
|
+
* publishes a `final` kind; the SDK keeps the slot reserved so consumers
|
|
160
|
+
* can register handlers today.
|
|
161
|
+
*/
|
|
162
|
+
'transcript.final': {
|
|
163
|
+
participantId: string;
|
|
164
|
+
speakerName: string;
|
|
165
|
+
text: string;
|
|
166
|
+
startedAt: string;
|
|
167
|
+
};
|
|
168
|
+
'system.message': SystemMessage;
|
|
169
|
+
'room.ended': undefined;
|
|
170
|
+
'track.subscribed': RoomTrackEvent;
|
|
171
|
+
'track.unsubscribed': RoomTrackEvent;
|
|
172
|
+
/** participantIds currently speaking (drives an active-speaker UI). */
|
|
173
|
+
'active.speakers': string[];
|
|
174
|
+
}
|
|
175
|
+
type Handler<E extends RoomEventName> = (payload: RoomEventPayloads[E]) => void;
|
|
176
|
+
interface RoomSession {
|
|
177
|
+
/** This session's own stable participant id (`p_…`). Useful to filter
|
|
178
|
+
* yourself out of `active.speakers`, which includes the local participant. */
|
|
179
|
+
readonly participantId: string;
|
|
180
|
+
/** Snapshot of the remote participants currently connected. */
|
|
181
|
+
readonly participants: RoomParticipantInfo[];
|
|
182
|
+
/** Subscribe to a typed event. No unsubscribe surface yet (mirrors `Call.onX`). */
|
|
183
|
+
on<E extends RoomEventName>(event: E, handler: Handler<E>): void;
|
|
184
|
+
/** Publish the local mic track. Resolves once the track is live on LiveKit. */
|
|
185
|
+
publishMic(): Promise<void>;
|
|
186
|
+
/** Publish the local camera track. */
|
|
187
|
+
publishCamera(): Promise<void>;
|
|
188
|
+
/** Mid-call mute/unmute of the local mic. */
|
|
189
|
+
setMicEnabled(on: boolean): Promise<void>;
|
|
190
|
+
/** Mid-call camera on/off. */
|
|
191
|
+
setCameraEnabled(on: boolean): Promise<void>;
|
|
192
|
+
/** Current local mic state (for toggle UI). */
|
|
193
|
+
isMicEnabled(): boolean;
|
|
194
|
+
/** Current local camera state (for toggle UI). */
|
|
195
|
+
isCameraEnabled(): boolean;
|
|
196
|
+
/** The local camera track for self-view, or null before publishCamera resolves. */
|
|
197
|
+
getLocalCameraTrack(): LocalVideoTrack | null;
|
|
198
|
+
/**
|
|
199
|
+
* Remote tracks already subscribed at this moment. A late joiner misses the
|
|
200
|
+
* live `track.subscribed` events for tracks published before it connected
|
|
201
|
+
* (LiveKit delivers them during `connect`, before consumer listeners attach).
|
|
202
|
+
* Call this right after registering `track.subscribed` to backfill them.
|
|
203
|
+
*/
|
|
204
|
+
getRemoteTracks(): RoomTrackEvent[];
|
|
205
|
+
/**
|
|
206
|
+
* Start/stop sharing the screen (via `getDisplayMedia`). Pass `{ audio: true }`
|
|
207
|
+
* to also capture shared/system audio where the browser allows it (Chrome:
|
|
208
|
+
* tab or system audio; macOS Chrome is tab-audio only; Safari/Firefox don't
|
|
209
|
+
* capture share audio). Publishes a `screen_share` video track (+ optional
|
|
210
|
+
* `screen_share_audio`); remote peers receive them via `track.subscribed`.
|
|
211
|
+
*/
|
|
212
|
+
setScreenShareEnabled(on: boolean, opts?: {
|
|
213
|
+
audio?: boolean;
|
|
214
|
+
}): Promise<void>;
|
|
215
|
+
/** Current local screen-share state (for toggle UI). */
|
|
216
|
+
isScreenShareEnabled(): boolean;
|
|
217
|
+
/** The local screen-share video track for self-preview, or null when off. */
|
|
218
|
+
getLocalScreenTrack(): LocalVideoTrack | null;
|
|
219
|
+
/** Disconnect from LiveKit. Idempotent. Triggers `room.ended` via Disconnected. */
|
|
220
|
+
leave(): Promise<void>;
|
|
221
|
+
}
|
|
222
|
+
declare const joinRoom: (opts: JoinRoomOptions) => Promise<RoomSession>;
|
|
223
|
+
|
|
82
224
|
interface FetchTokenArgs {
|
|
83
225
|
/** The agent the SDK is about to call. */
|
|
84
226
|
agentId: string;
|
|
@@ -228,6 +370,16 @@ interface VoiceClientFactory {
|
|
|
228
370
|
* don't reject this promise.
|
|
229
371
|
*/
|
|
230
372
|
startCall: (options: StartCallOptions) => Promise<Call>;
|
|
373
|
+
/**
|
|
374
|
+
* Phase 7 (multi-party rooms). Browser only. Exchange a single-use
|
|
375
|
+
* joinCode for a LiveKit JWT and connect to the room. The returned
|
|
376
|
+
* `RoomSession` exposes a typed event surface
|
|
377
|
+
* (participant.joined / participant.left / transcript.partial /
|
|
378
|
+
* transcript.final / system.message / room.ended) plus
|
|
379
|
+
* publishMic / publishCamera / leave. The Node bundle does NOT
|
|
380
|
+
* implement this — livekit-client is a browser-only WebRTC client.
|
|
381
|
+
*/
|
|
382
|
+
joinRoom?: (options: Omit<JoinRoomOptions, 'apiBase'>) => Promise<RoomSession>;
|
|
231
383
|
}
|
|
232
384
|
|
|
233
385
|
type OnChunk = (pcm: ArrayBuffer) => void;
|
|
@@ -307,6 +459,31 @@ declare const createReconnectingWebSocket: (options: RWSOptions, onEvent: (ev: R
|
|
|
307
459
|
};
|
|
308
460
|
type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>;
|
|
309
461
|
|
|
462
|
+
/**
|
|
463
|
+
* Canonical payload a tenant places in their VoIP/FCM push so an
|
|
464
|
+
* agent-initiated call can connect. It is the `callTokens.mint` result
|
|
465
|
+
* (token + transport) plus two optional display fields for the native
|
|
466
|
+
* incoming-call UI. The host receives the push, runs `parseIncomingCall`,
|
|
467
|
+
* and on accept passes `token` (+ transport / webrtcGatewayBase) into the
|
|
468
|
+
* voice client. Web background-wake is best-effort (Web Push); native is
|
|
469
|
+
* the real target. See docs/sdks.md "Agent-initiated calls".
|
|
470
|
+
*/
|
|
471
|
+
interface IncomingCallPayload {
|
|
472
|
+
token: string;
|
|
473
|
+
agentId: string;
|
|
474
|
+
transport: 'ws' | 'webrtc';
|
|
475
|
+
webrtcGatewayBase?: string;
|
|
476
|
+
expiresAt?: number;
|
|
477
|
+
agentName?: string;
|
|
478
|
+
agentAvatarUrl?: string;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Validate + normalise a raw push payload into an IncomingCallPayload.
|
|
482
|
+
* Throws synchronously on malformed input. Unknown transports fall back to
|
|
483
|
+
* 'ws'; webrtcGatewayBase is ignored unless transport === 'webrtc'.
|
|
484
|
+
*/
|
|
485
|
+
declare const parseIncomingCall: (raw: unknown) => IncomingCallPayload;
|
|
486
|
+
|
|
310
487
|
/**
|
|
311
488
|
* One-time SDK setup. Returns a factory you call `startCall` on for
|
|
312
489
|
* every voice call.
|
|
@@ -334,4 +511,4 @@ type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>;
|
|
|
334
511
|
*/
|
|
335
512
|
declare function configureVoiceClient(config: VoiceClientConfig): VoiceClientFactory;
|
|
336
513
|
|
|
337
|
-
export { type Call, type CallEndEvent, type CallEndReason, type CallError, type CallErrorCode, type CallState, type CaptureController, type CaptureOptions, type ClientTool, type ClientToolMap, type FetchToken, type FetchTokenArgs, type FetchTokenResult, type OnAgentSpeakingChange, type OnChunk, type OnError, type OnVolume$1 as OnVolume, type PlaybackController, type PlaybackOptions, 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, createAudioCapture, createAudioPlayback, createProtocolState, createReconnectingWebSocket, handleServerMessage };
|
|
514
|
+
export { type Call, type CallEndEvent, type CallEndReason, type CallError, type CallErrorCode, type CallState, type CaptureController, type CaptureOptions, type ClientTool, type ClientToolMap, type FetchToken, type FetchTokenArgs, type FetchTokenResult, type IncomingCallPayload, type JoinRoomOptions, type OnAgentSpeakingChange, type OnChunk, type OnError, type OnVolume$1 as OnVolume, type PlaybackController, type PlaybackOptions, type ProtocolCallbacks, type ProtocolState, type RWSEvent, type RWSOptions, type ReconnectingWebSocket, type RoomEventName, type RoomEventPayloads, type RoomParticipantInfo, type RoomSession, type ServerMessage, type StartCallOptions, type SystemMessage, type TranscriptEntry, type TranscriptMessage, type VoiceClientConfig, type VoiceClientFactory, type VolumeEvent, type WebSocketFactory, type WebSocketLike, buildWsUrl, configureVoiceClient, createAudioCapture, createAudioPlayback, createProtocolState, createReconnectingWebSocket, handleServerMessage, joinRoom, parseIncomingCall };
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RemoteTrack, LocalVideoTrack } from 'livekit-client'
|
|
2
|
+
|
|
1
3
|
interface ClientTool {
|
|
2
4
|
description: string
|
|
3
5
|
parameters: Record<string, unknown>
|
|
@@ -104,6 +106,167 @@ interface BuildWsUrlArgs {
|
|
|
104
106
|
}
|
|
105
107
|
declare function buildWsUrl(args: BuildWsUrlArgs): string
|
|
106
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>
|
|
267
|
+
}
|
|
268
|
+
declare const joinRoom: (opts: JoinRoomOptions) => Promise<RoomSession>
|
|
269
|
+
|
|
107
270
|
interface FetchTokenArgs {
|
|
108
271
|
/** The agent the SDK is about to call. */
|
|
109
272
|
agentId: string
|
|
@@ -253,6 +416,16 @@ interface VoiceClientFactory {
|
|
|
253
416
|
* don't reject this promise.
|
|
254
417
|
*/
|
|
255
418
|
startCall: (options: StartCallOptions) => Promise<Call>
|
|
419
|
+
/**
|
|
420
|
+
* Phase 7 (multi-party rooms). Browser only. Exchange a single-use
|
|
421
|
+
* joinCode for a LiveKit JWT and connect to the room. The returned
|
|
422
|
+
* `RoomSession` exposes a typed event surface
|
|
423
|
+
* (participant.joined / participant.left / transcript.partial /
|
|
424
|
+
* transcript.final / system.message / room.ended) plus
|
|
425
|
+
* publishMic / publishCamera / leave. The Node bundle does NOT
|
|
426
|
+
* implement this — livekit-client is a browser-only WebRTC client.
|
|
427
|
+
*/
|
|
428
|
+
joinRoom?: (options: Omit<JoinRoomOptions, 'apiBase'>) => Promise<RoomSession>
|
|
256
429
|
}
|
|
257
430
|
|
|
258
431
|
type OnChunk = (pcm: ArrayBuffer) => void
|
|
@@ -335,6 +508,31 @@ declare const createReconnectingWebSocket: (
|
|
|
335
508
|
}
|
|
336
509
|
type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>
|
|
337
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
|
+
|
|
338
536
|
/**
|
|
339
537
|
* One-time SDK setup. Returns a factory you call `startCall` on for
|
|
340
538
|
* every voice call.
|
|
@@ -376,6 +574,8 @@ export {
|
|
|
376
574
|
type FetchToken,
|
|
377
575
|
type FetchTokenArgs,
|
|
378
576
|
type FetchTokenResult,
|
|
577
|
+
type IncomingCallPayload,
|
|
578
|
+
type JoinRoomOptions,
|
|
379
579
|
type OnAgentSpeakingChange,
|
|
380
580
|
type OnChunk,
|
|
381
581
|
type OnError,
|
|
@@ -387,9 +587,15 @@ export {
|
|
|
387
587
|
type RWSEvent,
|
|
388
588
|
type RWSOptions,
|
|
389
589
|
type ReconnectingWebSocket,
|
|
590
|
+
type RoomEventName,
|
|
591
|
+
type RoomEventPayloads,
|
|
592
|
+
type RoomParticipantInfo,
|
|
593
|
+
type RoomSession,
|
|
390
594
|
type ServerMessage,
|
|
391
595
|
type StartCallOptions,
|
|
596
|
+
type SystemMessage,
|
|
392
597
|
type TranscriptEntry,
|
|
598
|
+
type TranscriptMessage,
|
|
393
599
|
type VoiceClientConfig,
|
|
394
600
|
type VoiceClientFactory,
|
|
395
601
|
type VolumeEvent,
|
|
@@ -402,4 +608,6 @@ export {
|
|
|
402
608
|
createProtocolState,
|
|
403
609
|
createReconnectingWebSocket,
|
|
404
610
|
handleServerMessage,
|
|
611
|
+
joinRoom,
|
|
612
|
+
parseIncomingCall,
|
|
405
613
|
}
|