@craftedxp/voice-js 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.mts +65 -1
- package/dist/browser.d.ts +467 -502
- package/dist/browser.js +921 -836
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +93 -1
- package/dist/browser.mjs.map +1 -1
- package/dist/embed.iife.js +67 -23358
- package/dist/node.d.mts +63 -0
- package/dist/node.d.ts +462 -483
- package/dist/node.js +453 -467
- package/dist/node.js.map +1 -1
- package/dist/node.mjs.map +1 -1
- package/package.json +2 -1
package/dist/node.d.ts
CHANGED
|
@@ -1,290 +1,298 @@
|
|
|
1
|
-
import { RemoteTrack, LocalVideoTrack } from 'livekit-client'
|
|
1
|
+
import { RemoteTrack, LocalVideoTrack } from 'livekit-client';
|
|
2
2
|
|
|
3
3
|
interface ClientTool {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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;
|
|
10
10
|
}
|
|
11
|
-
type ClientToolMap = Record<string, ClientTool
|
|
11
|
+
type ClientToolMap = Record<string, ClientTool>;
|
|
12
12
|
interface ClientToolCallFrame {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
toolCallId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
args: Record<string, unknown>;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
type CallState =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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'
|
|
18
|
+
type CallState = 'idle' | 'connecting' | 'listening' | 'user_speaking' | 'agent_speaking' | 'ended' | 'error';
|
|
19
|
+
type TranscriptEntry = {
|
|
20
|
+
id: string;
|
|
21
|
+
role: 'user';
|
|
22
|
+
text: string;
|
|
23
|
+
committed: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
id: string;
|
|
26
|
+
role: 'agent';
|
|
27
|
+
text: string;
|
|
28
|
+
interrupted?: boolean;
|
|
29
|
+
} | {
|
|
30
|
+
id: string;
|
|
31
|
+
role: 'tool';
|
|
32
|
+
text: string;
|
|
33
|
+
} | {
|
|
34
|
+
id: string;
|
|
35
|
+
role: 'system';
|
|
36
|
+
text: string;
|
|
37
|
+
};
|
|
38
|
+
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';
|
|
64
39
|
interface CallError {
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
code: CallErrorCode;
|
|
41
|
+
message: string;
|
|
67
42
|
}
|
|
68
|
-
type CallEndReason = 'agent_ended' | 'user_hangup' | 'timeout' | 'error'
|
|
43
|
+
type CallEndReason = 'agent_ended' | 'user_hangup' | 'timeout' | 'error';
|
|
69
44
|
interface CallEndEvent {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
45
|
+
reason: CallEndReason;
|
|
46
|
+
errorCode?: CallErrorCode;
|
|
47
|
+
durationMs: number;
|
|
73
48
|
}
|
|
74
49
|
interface VolumeEvent {
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
input: number;
|
|
51
|
+
output: number;
|
|
77
52
|
}
|
|
78
53
|
type ServerMessage = Record<string, unknown> & {
|
|
79
|
-
|
|
80
|
-
}
|
|
54
|
+
type?: string;
|
|
55
|
+
};
|
|
81
56
|
interface ProtocolState {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
57
|
+
state: CallState;
|
|
58
|
+
transcript: TranscriptEntry[];
|
|
59
|
+
agentBubbleId: string | null;
|
|
60
|
+
idCounter: number;
|
|
61
|
+
endReason: CallEndReason | null;
|
|
87
62
|
}
|
|
88
|
-
declare const createProtocolState: () => ProtocolState
|
|
63
|
+
declare const createProtocolState: () => ProtocolState;
|
|
89
64
|
interface ProtocolCallbacks {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
65
|
+
onState: (next: CallState) => void;
|
|
66
|
+
onTranscript: (entries: TranscriptEntry[]) => void;
|
|
67
|
+
onError: (err: CallError) => void;
|
|
68
|
+
onInterrupt: () => void;
|
|
69
|
+
onAgentTurnStart: (seq?: number) => void;
|
|
70
|
+
onAgentTurnEnd: (seq?: number) => void;
|
|
71
|
+
onCallEnd: (reason: CallEndReason) => void;
|
|
72
|
+
onConnected: () => void;
|
|
73
|
+
onClientToolCall: (frame: ClientToolCallFrame) => void;
|
|
99
74
|
}
|
|
100
|
-
declare function handleServerMessage(raw: string, state: ProtocolState, cb: ProtocolCallbacks): void
|
|
75
|
+
declare function handleServerMessage(raw: string, state: ProtocolState, cb: ProtocolCallbacks): void;
|
|
101
76
|
interface BuildWsUrlArgs {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
77
|
+
apiBase: string;
|
|
78
|
+
agentId: string;
|
|
79
|
+
token: string;
|
|
80
|
+
bargeIn?: boolean;
|
|
106
81
|
}
|
|
107
|
-
declare function buildWsUrl(args: BuildWsUrlArgs): string
|
|
82
|
+
declare function buildWsUrl(args: BuildWsUrlArgs): string;
|
|
108
83
|
|
|
109
|
-
type SystemMessage =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
kind: 'notetaker.connected'
|
|
140
|
-
}
|
|
141
|
-
| {
|
|
142
|
-
kind: 'notetaker.disconnected'
|
|
143
|
-
}
|
|
144
|
-
| {
|
|
145
|
-
kind: 'notetaker.partial_degraded'
|
|
146
|
-
participantId: string
|
|
147
|
-
}
|
|
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
|
+
};
|
|
148
114
|
type TranscriptMessage = {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
115
|
+
kind: 'partial';
|
|
116
|
+
participantId: string;
|
|
117
|
+
speakerName: string;
|
|
118
|
+
text: string;
|
|
119
|
+
startedAt: string;
|
|
120
|
+
};
|
|
155
121
|
|
|
156
122
|
interface JoinRoomOptions {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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;
|
|
165
131
|
}
|
|
166
132
|
interface RoomParticipantInfo {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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;
|
|
171
137
|
}
|
|
172
|
-
type RoomTrackKind = 'audio' | 'video'
|
|
138
|
+
type RoomTrackKind = 'audio' | 'video';
|
|
173
139
|
/** What a track is — lets consumers tell a camera apart from a screen share
|
|
174
140
|
* (a participant can publish both at once). Mirrors livekit `Track.Source`. */
|
|
175
|
-
type RoomTrackSource = 'camera' | 'microphone' | 'screen_share' | 'screen_share_audio' | 'unknown'
|
|
141
|
+
type RoomTrackSource = 'camera' | 'microphone' | 'screen_share' | 'screen_share_audio' | 'unknown';
|
|
176
142
|
interface RoomTrackEvent {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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;
|
|
184
150
|
}
|
|
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'
|
|
151
|
+
type RoomEventName = 'participant.joined' | 'participant.left' | 'transcript.partial' | 'transcript.final' | 'system.message' | 'room.ended' | 'track.subscribed' | 'track.unsubscribed' | 'active.speakers';
|
|
195
152
|
interface RoomEventPayloads {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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[];
|
|
217
174
|
}
|
|
218
|
-
type Handler<E extends RoomEventName> = (payload: RoomEventPayloads[E]) => void
|
|
175
|
+
type Handler<E extends RoomEventName> = (payload: RoomEventPayloads[E]) => void;
|
|
219
176
|
interface RoomSession {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Browser-friendly text-channel chat session. Mint a `ct_` token with
|
|
225
|
+
* `channel: 'text'` on your backend, then call `startTextSession({...})`
|
|
226
|
+
* to open the SSE stream.
|
|
227
|
+
*
|
|
228
|
+
* Each `.send(text)` is a fresh POST; SSE-per-turn means the connection
|
|
229
|
+
* closes when each turn ends. Conversation state lives server-side on the
|
|
230
|
+
* underlying CallRecord.
|
|
231
|
+
*/
|
|
232
|
+
type ChatEvent = {
|
|
233
|
+
type: 'chat.started';
|
|
234
|
+
chatId: string;
|
|
235
|
+
callId: string;
|
|
236
|
+
} | {
|
|
237
|
+
type: 'token';
|
|
238
|
+
text: string;
|
|
239
|
+
} | {
|
|
240
|
+
type: 'tool.call';
|
|
241
|
+
name: string;
|
|
242
|
+
args: unknown;
|
|
243
|
+
} | {
|
|
244
|
+
type: 'tool.result';
|
|
245
|
+
name: string;
|
|
246
|
+
ok?: boolean;
|
|
247
|
+
[key: string]: unknown;
|
|
248
|
+
} | {
|
|
249
|
+
type: 'turn.end';
|
|
250
|
+
finishReason: 'stop' | 'aborted' | 'length' | 'tool_error';
|
|
251
|
+
committedText?: string;
|
|
252
|
+
} | {
|
|
253
|
+
type: 'error';
|
|
254
|
+
code: string;
|
|
255
|
+
message: string;
|
|
256
|
+
};
|
|
257
|
+
interface StartTextSessionOpts {
|
|
258
|
+
baseUrl: string;
|
|
259
|
+
token: string;
|
|
260
|
+
agentId: string;
|
|
261
|
+
/** Optional inline first user message; otherwise the agent's greeting opens the stream. */
|
|
262
|
+
text?: string;
|
|
263
|
+
/** Override the global fetch (useful for tests; defaults to globalThis.fetch). */
|
|
264
|
+
fetch?: typeof fetch;
|
|
265
|
+
}
|
|
266
|
+
interface TextSession {
|
|
267
|
+
id: string;
|
|
268
|
+
callId: string;
|
|
269
|
+
/** Async iterable for the opening turn — greeting tokens / first reply if text was inlined. */
|
|
270
|
+
greeting: AsyncIterable<ChatEvent>;
|
|
271
|
+
/** Send a user message; returns an async iterable for the agent's reply. */
|
|
272
|
+
send(text: string): Promise<AsyncIterable<ChatEvent>>;
|
|
273
|
+
/** End the session — DELETE /v1/calls/:callId. */
|
|
274
|
+
end(): Promise<void>;
|
|
267
275
|
}
|
|
268
276
|
|
|
269
277
|
interface FetchTokenArgs {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
278
|
+
/** The agent the SDK is about to call. */
|
|
279
|
+
agentId: string;
|
|
280
|
+
/**
|
|
281
|
+
* Optional consumer-side user identifier. Round-tripped to the server
|
|
282
|
+
* as `contactId` for Phase 11 contact memory. The SDK does not
|
|
283
|
+
* inspect this; your backend uses it to scope the token mint.
|
|
284
|
+
*/
|
|
285
|
+
userId?: string;
|
|
286
|
+
/**
|
|
287
|
+
* Per-call structured context lowered into the agent's effective
|
|
288
|
+
* system prompt server-side at session open. Opaque to the SDK.
|
|
289
|
+
*/
|
|
290
|
+
context?: Record<string, unknown>;
|
|
291
|
+
/**
|
|
292
|
+
* String key/value pairs round-tripped on the `call.ended` webhook.
|
|
293
|
+
* Capped at 1 KB total server-side. NOT lowered into the system prompt.
|
|
294
|
+
*/
|
|
295
|
+
metadata?: Record<string, string>;
|
|
288
296
|
}
|
|
289
297
|
/**
|
|
290
298
|
* What `fetchToken` may return. The rich object form lets the server
|
|
@@ -292,209 +300,215 @@ interface FetchTokenArgs {
|
|
|
292
300
|
* compatible — the SDK treats it as `{ token, transport: 'ws' }`.
|
|
293
301
|
*/
|
|
294
302
|
interface FetchTokenResult {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
+
/** Raw `ct_` to feed into the WS open / WebRTC offer. */
|
|
304
|
+
token: string;
|
|
305
|
+
/** Server-selected transport. Default `'ws'` if absent. */
|
|
306
|
+
transport?: 'ws' | 'webrtc';
|
|
307
|
+
/** Required when `transport === 'webrtc'` AND the server uses a
|
|
308
|
+
* separate signaling gateway. When omitted on a webrtc result, the
|
|
309
|
+
* SDK falls back to the API base's Phase-1 routes (local dev). */
|
|
310
|
+
webrtcGatewayBase?: string;
|
|
303
311
|
}
|
|
304
|
-
type FetchToken = (args: FetchTokenArgs) => Promise<string | FetchTokenResult
|
|
312
|
+
type FetchToken = (args: FetchTokenArgs) => Promise<string | FetchTokenResult>;
|
|
305
313
|
interface VoiceClientConfig {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
314
|
+
/**
|
|
315
|
+
* Full HTTPS URL of the Voissia server. The WebSocket scheme is
|
|
316
|
+
* derived: `https` → `wss`, `http` → `ws`. No trailing slash needed.
|
|
317
|
+
*/
|
|
318
|
+
apiBase: string;
|
|
319
|
+
/**
|
|
320
|
+
* Called by the SDK whenever it needs a fresh `ct_` token (initial
|
|
321
|
+
* connect; mid-call refresh on `token_expired`). Your implementation
|
|
322
|
+
* should hit YOUR backend, which holds the `sk_` API key and mints
|
|
323
|
+
* via `POST /v1/call-tokens` (or `client.callTokens.mint` from
|
|
324
|
+
* @craftedxp/sdk-node). Never embed `sk_` in JS code that ships to a
|
|
325
|
+
* client.
|
|
326
|
+
*/
|
|
327
|
+
fetchToken: FetchToken;
|
|
328
|
+
/**
|
|
329
|
+
* Optional metadata applied to EVERY startCall. Per-call `metadata`
|
|
330
|
+
* in `startCall` is merged on top (per-call wins on key conflicts).
|
|
331
|
+
* Useful for dashboard-wide tags like `{ surface: 'web', appVersion }`.
|
|
332
|
+
*/
|
|
333
|
+
defaultMetadata?: Record<string, string>;
|
|
334
|
+
/**
|
|
335
|
+
* Optional context applied to EVERY startCall. Per-call `context` in
|
|
336
|
+
* `startCall` is merged on top. Useful for cross-call invariants like
|
|
337
|
+
* the signed-in user's locale.
|
|
338
|
+
*/
|
|
339
|
+
defaultContext?: Record<string, unknown>;
|
|
332
340
|
}
|
|
333
341
|
interface StartCallOptions {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
342
|
+
/** The agent to call. */
|
|
343
|
+
agentId: string;
|
|
344
|
+
/** Per-call user identifier. Round-tripped to fetchToken as `userId`. */
|
|
345
|
+
userId?: string;
|
|
346
|
+
/**
|
|
347
|
+
* Per-call structured context. Merged on top of `defaultContext`
|
|
348
|
+
* configured at factory time.
|
|
349
|
+
*/
|
|
350
|
+
context?: Record<string, unknown>;
|
|
351
|
+
/**
|
|
352
|
+
* Per-call metadata. Merged on top of `defaultMetadata` configured
|
|
353
|
+
* at factory time.
|
|
354
|
+
*/
|
|
355
|
+
metadata?: Record<string, string>;
|
|
356
|
+
/**
|
|
357
|
+
* When false, the SDK + server stay full-duplex but barge-in is
|
|
358
|
+
* suppressed. Useful for alarm-style flows where the user shouldn't
|
|
359
|
+
* accidentally interrupt the script. Default true.
|
|
360
|
+
*/
|
|
361
|
+
bargeIn?: boolean;
|
|
362
|
+
/**
|
|
363
|
+
* Client-side tools the agent's LLM can call mid-conversation. Each
|
|
364
|
+
* tool's handler runs on the consumer's side; result is fed back to
|
|
365
|
+
* the LLM through the existing call WebSocket. Schema and handler
|
|
366
|
+
* colocate. Validated synchronously at startCall — bad input throws.
|
|
367
|
+
*
|
|
368
|
+
* See docs/integration-echocheck.md for the wire protocol and the
|
|
369
|
+
* server-side guarantees.
|
|
370
|
+
*/
|
|
371
|
+
clientTools?: ClientToolMap;
|
|
372
|
+
/**
|
|
373
|
+
* Test-only escape hatch — pass a pre-minted `ct_` directly and skip
|
|
374
|
+
* the `fetchToken` call. Don't use this in production code: tokens
|
|
375
|
+
* expire and the SDK can't re-mint without the callback.
|
|
376
|
+
*/
|
|
377
|
+
token?: string;
|
|
378
|
+
onStateChange?: (state: CallState) => void;
|
|
379
|
+
onTranscript?: (entries: TranscriptEntry[]) => void;
|
|
380
|
+
onError?: (err: CallError) => void;
|
|
381
|
+
onEnd?: (end: CallEndEvent) => void;
|
|
382
|
+
/** Volume-meter event for VU UIs. ~10 Hz cadence (browser bundle only). */
|
|
383
|
+
onVolume?: (vol: VolumeEvent) => void;
|
|
384
|
+
/**
|
|
385
|
+
* Fires when the server signals barge-in (the user started talking
|
|
386
|
+
* mid-agent-turn). The browser bundle automatically flushes its
|
|
387
|
+
* built-in audio playback before this callback runs; the callback is
|
|
388
|
+
* fired regardless. Node / Electron consumers with custom playback
|
|
389
|
+
* should drain their audio queue here so the agent goes silent
|
|
390
|
+
* immediately.
|
|
391
|
+
*/
|
|
392
|
+
onInterrupt?: () => void;
|
|
393
|
+
/**
|
|
394
|
+
* Fires on `agent_turn_start` — the server has begun a new agent
|
|
395
|
+
* turn. The state-machine transition to `agent_speaking` happens at
|
|
396
|
+
* the same moment via `onStateChange`; use this when you want a
|
|
397
|
+
* precise turn anchor (e.g. "agent has been speaking for N ms" UIs)
|
|
398
|
+
* without diffing state.
|
|
399
|
+
*/
|
|
400
|
+
onAgentTurnStart?: () => void;
|
|
393
401
|
}
|
|
394
402
|
interface Call {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
403
|
+
/** Current state. Snapshot — subscribe via onStateChange for live updates. */
|
|
404
|
+
readonly state: CallState;
|
|
405
|
+
/** Full transcript so far. Snapshot — subscribe via onTranscript for live updates. */
|
|
406
|
+
readonly transcript: TranscriptEntry[];
|
|
407
|
+
/** True after `mute()` and before `unmute()`. */
|
|
408
|
+
readonly isMuted: boolean;
|
|
409
|
+
/** End the call locally. Closes the WS, stops the mic, fires onEnd. Idempotent. */
|
|
410
|
+
end: () => void;
|
|
411
|
+
/** Mute mic frames. Wire stays active so server endpointing doesn't false-positive. Idempotent. */
|
|
412
|
+
mute: () => void;
|
|
413
|
+
/** Unmute mic frames. Idempotent. */
|
|
414
|
+
unmute: () => void;
|
|
407
415
|
}
|
|
408
416
|
interface VoiceClientFactory {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
417
|
+
/** Read back the resolved config (post trailing-slash normalisation). */
|
|
418
|
+
readonly config: VoiceClientConfig;
|
|
419
|
+
/**
|
|
420
|
+
* Open a fresh call. Returns when the WS is open; rejects on
|
|
421
|
+
* pre-flight failure (missing config, fetchToken throw, etc). Mid-
|
|
422
|
+
* call failures arrive via the per-call `onError` callback — they
|
|
423
|
+
* don't reject this promise.
|
|
424
|
+
*/
|
|
425
|
+
startCall: (options: StartCallOptions) => Promise<Call>;
|
|
426
|
+
/**
|
|
427
|
+
* Phase 7 (multi-party rooms). Browser only. Exchange a single-use
|
|
428
|
+
* joinCode for a LiveKit JWT and connect to the room. The returned
|
|
429
|
+
* `RoomSession` exposes a typed event surface
|
|
430
|
+
* (participant.joined / participant.left / transcript.partial /
|
|
431
|
+
* transcript.final / system.message / room.ended) plus
|
|
432
|
+
* publishMic / publishCamera / leave. The Node bundle does NOT
|
|
433
|
+
* implement this — livekit-client is a browser-only WebRTC client.
|
|
434
|
+
*/
|
|
435
|
+
joinRoom?: (options: Omit<JoinRoomOptions, 'apiBase'>) => Promise<RoomSession>;
|
|
436
|
+
/**
|
|
437
|
+
* Open a text-channel chat session (no microphone / audio required).
|
|
438
|
+
* Mint a `ct_` token with `channel: 'text'` server-side, then call
|
|
439
|
+
* this to connect. Returns a `TextSession` with:
|
|
440
|
+
* - `.greeting` — async iterable for the opening turn
|
|
441
|
+
* - `.send(text)` — send a user message; returns an async iterable for the reply
|
|
442
|
+
* - `.end()` — close the session (DELETE /v1/calls/:callId)
|
|
443
|
+
*/
|
|
444
|
+
startTextSession?: (opts: Omit<StartTextSessionOpts, 'baseUrl' | 'fetch'>) => Promise<TextSession>;
|
|
428
445
|
}
|
|
429
446
|
|
|
430
|
-
type RWSEvent =
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
| {
|
|
448
|
-
type: 'error'
|
|
449
|
-
error: Error
|
|
450
|
-
}
|
|
447
|
+
type RWSEvent = {
|
|
448
|
+
type: 'open';
|
|
449
|
+
} | {
|
|
450
|
+
type: 'reconnected';
|
|
451
|
+
} | {
|
|
452
|
+
type: 'message';
|
|
453
|
+
data: string | ArrayBuffer;
|
|
454
|
+
} | {
|
|
455
|
+
type: 'close';
|
|
456
|
+
code: number;
|
|
457
|
+
reason: string;
|
|
458
|
+
permanent: boolean;
|
|
459
|
+
} | {
|
|
460
|
+
type: 'error';
|
|
461
|
+
error: Error;
|
|
462
|
+
};
|
|
451
463
|
interface WebSocketLike {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
464
|
+
binaryType: string;
|
|
465
|
+
readyState: number;
|
|
466
|
+
onopen: ((ev: unknown) => void) | null;
|
|
467
|
+
onmessage: ((ev: {
|
|
468
|
+
data: string | ArrayBuffer;
|
|
469
|
+
}) => void) | null;
|
|
470
|
+
onerror: ((ev: unknown) => void) | null;
|
|
471
|
+
onclose: ((ev: {
|
|
472
|
+
code: number;
|
|
473
|
+
reason: string;
|
|
474
|
+
}) => void) | null;
|
|
475
|
+
send: (data: string | ArrayBuffer | ArrayBufferView) => void;
|
|
476
|
+
close: (code?: number, reason?: string) => void;
|
|
460
477
|
}
|
|
461
|
-
type WebSocketFactory = (url: string) => WebSocketLike
|
|
478
|
+
type WebSocketFactory = (url: string) => WebSocketLike;
|
|
462
479
|
interface RWSOptions {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
480
|
+
url: string;
|
|
481
|
+
wsFactory: WebSocketFactory;
|
|
482
|
+
maxRetries?: number;
|
|
483
|
+
initialBackoffMs?: number;
|
|
484
|
+
maxBackoffMs?: number;
|
|
468
485
|
}
|
|
469
|
-
declare const createReconnectingWebSocket: (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
) =>
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
readyState: () => number
|
|
476
|
-
}
|
|
477
|
-
type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>
|
|
486
|
+
declare const createReconnectingWebSocket: (options: RWSOptions, onEvent: (ev: RWSEvent) => void) => {
|
|
487
|
+
send: (data: string | ArrayBuffer | ArrayBufferView) => void;
|
|
488
|
+
close: (code?: number, reason?: string) => void;
|
|
489
|
+
readyState: () => number;
|
|
490
|
+
};
|
|
491
|
+
type ReconnectingWebSocket = ReturnType<typeof createReconnectingWebSocket>;
|
|
478
492
|
|
|
479
493
|
interface NodeStartCallOptions extends StartCallOptions {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
494
|
+
/**
|
|
495
|
+
* Fires for each binary PCM frame the server pushes (Int16 LE mono
|
|
496
|
+
* @ 16 kHz — same as the browser playback path). Wire to your
|
|
497
|
+
* preferred output: write to a `sox -t raw -r 16000 -e signed -b 16
|
|
498
|
+
* -c 1 - default` subprocess, queue into PortAudio, relay over RTP,
|
|
499
|
+
* etc. If you don't supply this callback, agent audio is dropped on
|
|
500
|
+
* the floor.
|
|
501
|
+
*/
|
|
502
|
+
onAudioChunk?: (pcm: ArrayBuffer) => void;
|
|
489
503
|
}
|
|
490
504
|
interface NodeCall extends Call {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
505
|
+
/**
|
|
506
|
+
* Push one mic frame to the server. Expected: Int16 LE mono PCM @
|
|
507
|
+
* 16 kHz. Capture cadence ~100 ms / ~3.2 KB per frame is fine.
|
|
508
|
+
* Returns `false` if the WS isn't open yet (caller may want to
|
|
509
|
+
* back-pressure or drop).
|
|
510
|
+
*/
|
|
511
|
+
sendAudioChunk: (pcm: ArrayBuffer | ArrayBufferView) => boolean;
|
|
498
512
|
}
|
|
499
513
|
/**
|
|
500
514
|
* Node bundle's analog of `VoiceClientFactory`. Same shape but
|
|
@@ -504,8 +518,8 @@ interface NodeCall extends Call {
|
|
|
504
518
|
* entry. Browser entry returns the base `VoiceClientFactory` type.
|
|
505
519
|
*/
|
|
506
520
|
interface NodeVoiceClientFactory {
|
|
507
|
-
|
|
508
|
-
|
|
521
|
+
readonly config: VoiceClientConfig;
|
|
522
|
+
startCall: (options: NodeStartCallOptions) => Promise<NodeCall>;
|
|
509
523
|
}
|
|
510
524
|
|
|
511
525
|
/**
|
|
@@ -518,20 +532,20 @@ interface NodeVoiceClientFactory {
|
|
|
518
532
|
* the real target. See docs/sdks.md "Agent-initiated calls".
|
|
519
533
|
*/
|
|
520
534
|
interface IncomingCallPayload {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
535
|
+
token: string;
|
|
536
|
+
agentId: string;
|
|
537
|
+
transport: 'ws' | 'webrtc';
|
|
538
|
+
webrtcGatewayBase?: string;
|
|
539
|
+
expiresAt?: number;
|
|
540
|
+
agentName?: string;
|
|
541
|
+
agentAvatarUrl?: string;
|
|
528
542
|
}
|
|
529
543
|
/**
|
|
530
544
|
* Validate + normalise a raw push payload into an IncomingCallPayload.
|
|
531
545
|
* Throws synchronously on malformed input. Unknown transports fall back to
|
|
532
546
|
* 'ws'; webrtcGatewayBase is ignored unless transport === 'webrtc'.
|
|
533
547
|
*/
|
|
534
|
-
declare const parseIncomingCall: (raw: unknown) => IncomingCallPayload
|
|
548
|
+
declare const parseIncomingCall: (raw: unknown) => IncomingCallPayload;
|
|
535
549
|
|
|
536
550
|
/**
|
|
537
551
|
* One-time SDK setup for Node.js / Electron-main consumers. Returns a
|
|
@@ -561,41 +575,6 @@ declare const parseIncomingCall: (raw: unknown) => IncomingCallPayload
|
|
|
561
575
|
*
|
|
562
576
|
* mic.stdout.on('data', (chunk) => call.sendAudioChunk(chunk))
|
|
563
577
|
*/
|
|
564
|
-
declare function configureVoiceClient(config: VoiceClientConfig): NodeVoiceClientFactory
|
|
578
|
+
declare function configureVoiceClient(config: VoiceClientConfig): NodeVoiceClientFactory;
|
|
565
579
|
|
|
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
|
-
}
|
|
580
|
+
export { type Call, type CallEndEvent, type CallEndReason, type CallError, type CallErrorCode, type CallState, type ClientTool, type ClientToolMap, type FetchToken, type FetchTokenArgs, type FetchTokenResult, type IncomingCallPayload, 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, parseIncomingCall };
|