@craftedxp/voice-js 0.5.4 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CONSUMING.md +6 -2
  2. package/README.md +31 -4
  3. package/dist/assistant.d.mts +32 -0
  4. package/dist/assistant.d.ts +32 -0
  5. package/dist/assistant.js +1241 -0
  6. package/dist/assistant.js.map +1 -0
  7. package/dist/assistant.mjs +23 -0
  8. package/dist/assistant.mjs.map +1 -0
  9. package/dist/browser.d.mts +12 -509
  10. package/dist/browser.d.ts +12 -608
  11. package/dist/browser.js +1020 -896
  12. package/dist/browser.js.map +1 -1
  13. package/dist/browser.mjs +25 -1283
  14. package/dist/browser.mjs.map +1 -1
  15. package/dist/chunk-LV7JGPYW.mjs +200 -0
  16. package/dist/chunk-LV7JGPYW.mjs.map +1 -0
  17. package/dist/chunk-ZW22Y67M.mjs +1208 -0
  18. package/dist/chunk-ZW22Y67M.mjs.map +1 -0
  19. package/dist/config-D2TbvIqT.d.mts +297 -0
  20. package/dist/config-D2TbvIqT.d.ts +297 -0
  21. package/dist/embed.iife.js +30 -23358
  22. package/dist/incomingCall-CfRRzj2P.d.mts +103 -0
  23. package/dist/incomingCall-CfRRzj2P.d.ts +103 -0
  24. package/dist/node.d.mts +69 -139
  25. package/dist/node.d.ts +342 -496
  26. package/dist/node.js +472 -467
  27. package/dist/node.js.map +1 -1
  28. package/dist/node.mjs +19 -0
  29. package/dist/node.mjs.map +1 -1
  30. package/dist/room.d.mts +156 -0
  31. package/dist/room.d.ts +156 -0
  32. package/dist/room.js +236 -0
  33. package/dist/room.js.map +1 -0
  34. package/dist/room.mjs +7 -0
  35. package/dist/room.mjs.map +1 -0
  36. package/dist/transcribe.d.mts +14 -0
  37. package/dist/transcribe.d.ts +14 -0
  38. package/dist/transcribe.js +1213 -0
  39. package/dist/transcribe.js.map +1 -0
  40. package/dist/transcribe.mjs +18 -0
  41. package/dist/transcribe.mjs.map +1 -0
  42. package/package.json +22 -4
@@ -0,0 +1,297 @@
1
+ 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;
8
+ }
9
+ type ClientToolMap = Record<string, ClientTool>;
10
+ interface ClientToolCallFrame {
11
+ toolCallId: string;
12
+ name: string;
13
+ args: Record<string, unknown>;
14
+ }
15
+
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';
37
+ interface CallError {
38
+ code: CallErrorCode;
39
+ message: string;
40
+ }
41
+ type CallEndReason = 'agent_ended' | 'user_hangup' | 'timeout' | 'error';
42
+ interface CallEndEvent {
43
+ reason: CallEndReason;
44
+ errorCode?: CallErrorCode;
45
+ durationMs: number;
46
+ }
47
+ interface VolumeEvent {
48
+ input: number;
49
+ output: number;
50
+ }
51
+ type ServerMessage = Record<string, unknown> & {
52
+ type?: string;
53
+ };
54
+ interface ProtocolState {
55
+ state: CallState;
56
+ transcript: TranscriptEntry[];
57
+ agentBubbleId: string | null;
58
+ idCounter: number;
59
+ endReason: CallEndReason | null;
60
+ }
61
+ declare const createProtocolState: () => ProtocolState;
62
+ 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;
72
+ }
73
+ declare function handleServerMessage(raw: string, state: ProtocolState, cb: ProtocolCallbacks): void;
74
+ interface BuildWsUrlArgs {
75
+ apiBase: string;
76
+ agentId: string;
77
+ token: string;
78
+ bargeIn?: boolean;
79
+ }
80
+ declare function buildWsUrl(args: BuildWsUrlArgs): string;
81
+
82
+ /**
83
+ * Browser-friendly text-channel chat session. Mint a `ct_` token with
84
+ * `channel: 'text'` on your backend, then call `startTextSession({...})`
85
+ * to open the SSE stream.
86
+ *
87
+ * Each `.send(text)` is a fresh POST; SSE-per-turn means the connection
88
+ * closes when each turn ends. Conversation state lives server-side on the
89
+ * underlying CallRecord.
90
+ */
91
+ type ChatEvent = {
92
+ type: 'chat.started';
93
+ chatId: string;
94
+ callId: string;
95
+ } | {
96
+ type: 'token';
97
+ text: string;
98
+ } | {
99
+ type: 'tool.call';
100
+ name: string;
101
+ args: unknown;
102
+ } | {
103
+ type: 'tool.result';
104
+ name: string;
105
+ ok?: boolean;
106
+ [key: string]: unknown;
107
+ } | {
108
+ type: 'turn.end';
109
+ finishReason: 'stop' | 'aborted' | 'length' | 'tool_error';
110
+ committedText?: string;
111
+ } | {
112
+ type: 'error';
113
+ code: string;
114
+ message: string;
115
+ };
116
+ interface StartTextSessionOpts {
117
+ baseUrl: string;
118
+ token: string;
119
+ agentId: string;
120
+ /** Optional inline first user message; otherwise the agent's greeting opens the stream. */
121
+ text?: string;
122
+ /** Override the global fetch (useful for tests; defaults to globalThis.fetch). */
123
+ fetch?: typeof fetch;
124
+ }
125
+ interface TextSession {
126
+ id: string;
127
+ callId: string;
128
+ /** Async iterable for the opening turn — greeting tokens / first reply if text was inlined. */
129
+ greeting: AsyncIterable<ChatEvent>;
130
+ /** Send a user message; returns an async iterable for the agent's reply. */
131
+ send(text: string): Promise<AsyncIterable<ChatEvent>>;
132
+ /** End the session — DELETE /v1/calls/:callId. */
133
+ end(): Promise<void>;
134
+ }
135
+ declare function startTextSession(opts: StartTextSessionOpts): Promise<TextSession>;
136
+
137
+ interface FetchTokenArgs {
138
+ /** The agent the SDK is about to call. */
139
+ agentId: string;
140
+ /**
141
+ * Optional consumer-side user identifier. Round-tripped to the server
142
+ * as `contactId` for Phase 11 contact memory. The SDK does not
143
+ * inspect this; your backend uses it to scope the token mint.
144
+ */
145
+ userId?: string;
146
+ /**
147
+ * Per-call structured context lowered into the agent's effective
148
+ * system prompt server-side at session open. Opaque to the SDK.
149
+ */
150
+ context?: Record<string, unknown>;
151
+ /**
152
+ * String key/value pairs round-tripped on the `call.ended` webhook.
153
+ * Capped at 1 KB total server-side. NOT lowered into the system prompt.
154
+ */
155
+ metadata?: Record<string, string>;
156
+ }
157
+ /**
158
+ * What `fetchToken` may return. The rich object form lets the server
159
+ * choose the transport per call. Returning a bare string is backwards-
160
+ * compatible — the SDK treats it as `{ token, transport: 'ws' }`.
161
+ */
162
+ interface FetchTokenResult {
163
+ /** Raw `ct_` to feed into the WS open / WebRTC offer. */
164
+ token: string;
165
+ /** Server-selected transport. Default `'ws'` if absent. */
166
+ transport?: 'ws' | 'webrtc';
167
+ /** Required when `transport === 'webrtc'` AND the server uses a
168
+ * separate signaling gateway. When omitted on a webrtc result, the
169
+ * SDK falls back to the API base's Phase-1 routes (local dev). */
170
+ webrtcGatewayBase?: string;
171
+ }
172
+ type FetchToken = (args: FetchTokenArgs) => Promise<string | FetchTokenResult>;
173
+ interface VoiceClientConfig {
174
+ /**
175
+ * Full HTTPS URL of the Voissia server. The WebSocket scheme is
176
+ * derived: `https` → `wss`, `http` → `ws`. No trailing slash needed.
177
+ */
178
+ apiBase: string;
179
+ /**
180
+ * Called by the SDK whenever it needs a fresh `ct_` token (initial
181
+ * connect; mid-call refresh on `token_expired`). Your implementation
182
+ * should hit YOUR backend, which holds the `sk_` API key and mints
183
+ * via `POST /v1/call-tokens` (or `client.callTokens.mint` from
184
+ * @craftedxp/sdk-node). Never embed `sk_` in JS code that ships to a
185
+ * client.
186
+ */
187
+ fetchToken: FetchToken;
188
+ /**
189
+ * Optional metadata applied to EVERY startCall. Per-call `metadata`
190
+ * in `startCall` is merged on top (per-call wins on key conflicts).
191
+ * Useful for dashboard-wide tags like `{ surface: 'web', appVersion }`.
192
+ */
193
+ defaultMetadata?: Record<string, string>;
194
+ /**
195
+ * Optional context applied to EVERY startCall. Per-call `context` in
196
+ * `startCall` is merged on top. Useful for cross-call invariants like
197
+ * the signed-in user's locale.
198
+ */
199
+ defaultContext?: Record<string, unknown>;
200
+ }
201
+ interface StartCallOptions {
202
+ /** The agent to call. */
203
+ agentId: string;
204
+ /** Per-call user identifier. Round-tripped to fetchToken as `userId`. */
205
+ userId?: string;
206
+ /**
207
+ * Per-call structured context. Merged on top of `defaultContext`
208
+ * configured at factory time.
209
+ */
210
+ context?: Record<string, unknown>;
211
+ /**
212
+ * Per-call metadata. Merged on top of `defaultMetadata` configured
213
+ * at factory time.
214
+ */
215
+ metadata?: Record<string, string>;
216
+ /**
217
+ * When false, the SDK + server stay full-duplex but barge-in is
218
+ * suppressed. Useful for alarm-style flows where the user shouldn't
219
+ * accidentally interrupt the script. Default true.
220
+ */
221
+ bargeIn?: boolean;
222
+ /**
223
+ * Client-side tools the agent's LLM can call mid-conversation. Each
224
+ * tool's handler runs on the consumer's side; result is fed back to
225
+ * the LLM through the existing call WebSocket. Schema and handler
226
+ * colocate. Validated synchronously at startCall — bad input throws.
227
+ *
228
+ * See docs/sdks.md ("Client tools") for the wire protocol and the
229
+ * server-side guarantees.
230
+ */
231
+ clientTools?: ClientToolMap;
232
+ /**
233
+ * Test-only escape hatch — pass a pre-minted `ct_` directly and skip
234
+ * the `fetchToken` call. Don't use this in production code: tokens
235
+ * expire and the SDK can't re-mint without the callback.
236
+ */
237
+ token?: string;
238
+ onStateChange?: (state: CallState) => void;
239
+ onTranscript?: (entries: TranscriptEntry[]) => void;
240
+ onError?: (err: CallError) => void;
241
+ onEnd?: (end: CallEndEvent) => void;
242
+ /** Volume-meter event for VU UIs. ~10 Hz cadence (browser bundle only). */
243
+ onVolume?: (vol: VolumeEvent) => void;
244
+ /**
245
+ * Fires when the server signals barge-in (the user started talking
246
+ * mid-agent-turn). The browser bundle automatically flushes its
247
+ * built-in audio playback before this callback runs; the callback is
248
+ * fired regardless. Node / Electron consumers with custom playback
249
+ * should drain their audio queue here so the agent goes silent
250
+ * immediately.
251
+ */
252
+ onInterrupt?: () => void;
253
+ /**
254
+ * Fires on `agent_turn_start` — the server has begun a new agent
255
+ * turn. The state-machine transition to `agent_speaking` happens at
256
+ * the same moment via `onStateChange`; use this when you want a
257
+ * precise turn anchor (e.g. "agent has been speaking for N ms" UIs)
258
+ * without diffing state.
259
+ */
260
+ onAgentTurnStart?: () => void;
261
+ }
262
+ interface Call {
263
+ /** Current state. Snapshot — subscribe via onStateChange for live updates. */
264
+ readonly state: CallState;
265
+ /** Full transcript so far. Snapshot — subscribe via onTranscript for live updates. */
266
+ readonly transcript: TranscriptEntry[];
267
+ /** True after `mute()` and before `unmute()`. */
268
+ readonly isMuted: boolean;
269
+ /** End the call locally. Closes the WS, stops the mic, fires onEnd. Idempotent. */
270
+ end: () => void;
271
+ /** Mute mic frames. Wire stays active so server endpointing doesn't false-positive. Idempotent. */
272
+ mute: () => void;
273
+ /** Unmute mic frames. Idempotent. */
274
+ unmute: () => void;
275
+ }
276
+ interface VoiceClientFactory {
277
+ /** Read back the resolved config (post trailing-slash normalisation). */
278
+ readonly config: VoiceClientConfig;
279
+ /**
280
+ * Open a fresh call. Returns when the WS is open; rejects on
281
+ * pre-flight failure (missing config, fetchToken throw, etc). Mid-
282
+ * call failures arrive via the per-call `onError` callback — they
283
+ * don't reject this promise.
284
+ */
285
+ startCall: (options: StartCallOptions) => Promise<Call>;
286
+ /**
287
+ * Open a text-channel chat session (no microphone / audio required).
288
+ * Mint a `ct_` token with `channel: 'text'` server-side, then call
289
+ * this to connect. Returns a `TextSession` with:
290
+ * - `.greeting` — async iterable for the opening turn
291
+ * - `.send(text)` — send a user message; returns an async iterable for the reply
292
+ * - `.end()` — close the session (DELETE /v1/calls/:callId)
293
+ */
294
+ startTextSession?: (opts: Omit<StartTextSessionOpts, 'baseUrl' | 'fetch'>) => Promise<TextSession>;
295
+ }
296
+
297
+ export { type Call as C, type FetchToken as F, type ProtocolCallbacks as P, type ServerMessage as S, type TextSession as T, type VoiceClientConfig as V, type VoiceClientFactory as a, type CallEndEvent as b, type CallEndReason as c, type CallError as d, type CallErrorCode as e, type CallState as f, type ChatEvent as g, type ClientTool as h, type ClientToolMap as i, type FetchTokenArgs as j, type FetchTokenResult as k, type ProtocolState as l, type StartCallOptions as m, type StartTextSessionOpts as n, type TranscriptEntry as o, type VolumeEvent as p, buildWsUrl as q, createProtocolState as r, handleServerMessage as s, startTextSession as t };