realtime-voice-agents 2.0.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -0
  3. package/assets/elevator-jazz.ulaw +1 -0
  4. package/assets/keyboard-typing.ulaw +1 -0
  5. package/assets/lofi.ulaw +1 -0
  6. package/assets/ringing.ulaw +1 -0
  7. package/assets/thinking-hum.ulaw +1 -0
  8. package/dist/BackgroundAudioPlayer-iMcivjis.mjs +282 -0
  9. package/dist/BackgroundAudioPlayer-jfRULWKC.cjs +313 -0
  10. package/dist/BaseRealtimeProvider-BQigr5mB.mjs +62 -0
  11. package/dist/BaseRealtimeProvider-BehPNT1r.d.cts +239 -0
  12. package/dist/BaseRealtimeProvider-ClP8Wx1X.d.mts +239 -0
  13. package/dist/BaseRealtimeProvider-DI4pKtOb.cjs +91 -0
  14. package/dist/GeminiLiveProvider-DvkTgzjG.d.cts +101 -0
  15. package/dist/GeminiLiveProvider-x2nyx5aO.d.mts +101 -0
  16. package/dist/InMemorySessionStore-B0_i-DOU.cjs +34 -0
  17. package/dist/InMemorySessionStore-B5_rq61L.d.cts +83 -0
  18. package/dist/InMemorySessionStore-B5_rq61L.d.mts +83 -0
  19. package/dist/InMemorySessionStore-DYParOJO.mjs +29 -0
  20. package/dist/OpenAICompatibleProvider-Bdtl-UXH.mjs +402 -0
  21. package/dist/OpenAICompatibleProvider-NS4cKVQj.cjs +409 -0
  22. package/dist/audio.cjs +21 -0
  23. package/dist/audio.d.cts +165 -0
  24. package/dist/audio.d.mts +165 -0
  25. package/dist/audio.mjs +4 -0
  26. package/dist/env-DSnGaERV.cjs +19 -0
  27. package/dist/env-DUwUWTsg.mjs +14 -0
  28. package/dist/events-BUMYdETO.d.cts +24 -0
  29. package/dist/events-BUMYdETO.d.mts +24 -0
  30. package/dist/events-BxDTIKKq.cjs +48 -0
  31. package/dist/events-BylBSBW-.mjs +43 -0
  32. package/dist/gemini.cjs +401 -0
  33. package/dist/gemini.d.cts +41 -0
  34. package/dist/gemini.d.mts +41 -0
  35. package/dist/gemini.mjs +395 -0
  36. package/dist/index.cjs +2142 -0
  37. package/dist/index.d.cts +920 -0
  38. package/dist/index.d.mts +920 -0
  39. package/dist/index.mjs +2113 -0
  40. package/dist/mulaw--cwU2c9L.mjs +64 -0
  41. package/dist/mulaw-DLUObjdP.cjs +117 -0
  42. package/dist/openai.cjs +61 -0
  43. package/dist/openai.d.cts +121 -0
  44. package/dist/openai.d.mts +121 -0
  45. package/dist/openai.mjs +53 -0
  46. package/dist/presets-Bf75YXs5.d.cts +24 -0
  47. package/dist/presets-Bf75YXs5.d.mts +24 -0
  48. package/dist/rest-BYqiVOhe.mjs +265 -0
  49. package/dist/rest-BvUKut_k.cjs +300 -0
  50. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  51. package/dist/session-config-BVLl7-ha.mjs +74 -0
  52. package/dist/session-config-c8sOw1XL.cjs +85 -0
  53. package/dist/store.cjs +3 -0
  54. package/dist/store.d.cts +2 -0
  55. package/dist/store.d.mts +2 -0
  56. package/dist/store.mjs +2 -0
  57. package/dist/testing.cjs +544 -0
  58. package/dist/testing.d.cts +191 -0
  59. package/dist/testing.d.mts +191 -0
  60. package/dist/testing.mjs +538 -0
  61. package/dist/transcode-C9aJG7_W.cjs +215 -0
  62. package/dist/transcode-CsAp97G9.mjs +198 -0
  63. package/dist/transport-B_PJFIVd.d.cts +155 -0
  64. package/dist/transport-CEaLFV4E.d.mts +155 -0
  65. package/dist/twilio.cjs +8 -0
  66. package/dist/twilio.d.cts +3 -0
  67. package/dist/twilio.d.mts +3 -0
  68. package/dist/twilio.mjs +2 -0
  69. package/dist/twiml-z9LjoF4_.d.cts +67 -0
  70. package/dist/twiml-z9LjoF4_.d.mts +67 -0
  71. package/dist/xai.cjs +92 -0
  72. package/dist/xai.d.cts +35 -0
  73. package/dist/xai.d.mts +35 -0
  74. package/dist/xai.mjs +86 -0
  75. package/package.json +183 -0
@@ -0,0 +1,155 @@
1
+ import { t as TypedEmitter } from "./events-BUMYdETO.mjs";
2
+ //#region src/twilio/messages.d.ts
3
+ /**
4
+ * Twilio Media Streams wire messages.
5
+ * Reference: https://www.twilio.com/docs/voice/media-streams/websocket-messages
6
+ *
7
+ * Inbound (Twilio → server): connected, start, media, stop, mark, dtmf.
8
+ * Outbound (server → Twilio): media, mark, clear.
9
+ * All media payloads are base64 μ-law 8 kHz mono on `<Connect><Stream>` calls.
10
+ */
11
+ interface TwilioConnectedEvent {
12
+ event: 'connected';
13
+ protocol?: string;
14
+ version?: string;
15
+ }
16
+ interface TwilioStartEvent {
17
+ event: 'start';
18
+ sequenceNumber?: string;
19
+ streamSid: string;
20
+ start: {
21
+ streamSid: string;
22
+ accountSid?: string;
23
+ callSid: string;
24
+ tracks?: string[];
25
+ mediaFormat?: {
26
+ encoding: string;
27
+ sampleRate: number;
28
+ channels: number;
29
+ };
30
+ /** `<Parameter name="…" value="…"/>` pairs from the TwiML `<Stream>`. */
31
+ customParameters?: Record<string, string>;
32
+ };
33
+ }
34
+ interface TwilioMediaEvent {
35
+ event: 'media';
36
+ sequenceNumber?: string;
37
+ streamSid: string;
38
+ media: {
39
+ track?: 'inbound' | 'outbound';
40
+ chunk?: string;
41
+ /** Milliseconds since stream start — the honest wall clock of the call. */
42
+ timestamp?: string;
43
+ /** Base64 μ-law audio. */
44
+ payload: string;
45
+ };
46
+ }
47
+ interface TwilioStopEvent {
48
+ event: 'stop';
49
+ sequenceNumber?: string;
50
+ streamSid: string;
51
+ stop?: {
52
+ accountSid?: string;
53
+ callSid?: string;
54
+ };
55
+ }
56
+ interface TwilioMarkEvent {
57
+ event: 'mark';
58
+ sequenceNumber?: string;
59
+ streamSid: string;
60
+ mark: {
61
+ name: string;
62
+ };
63
+ }
64
+ interface TwilioDtmfEvent {
65
+ event: 'dtmf';
66
+ streamSid: string;
67
+ sequenceNumber?: string;
68
+ dtmf: {
69
+ track?: string;
70
+ digit: string;
71
+ };
72
+ }
73
+ type TwilioInboundMessage = TwilioConnectedEvent | TwilioStartEvent | TwilioMediaEvent | TwilioStopEvent | TwilioMarkEvent | TwilioDtmfEvent;
74
+ interface TwilioOutboundMedia {
75
+ event: 'media';
76
+ streamSid: string;
77
+ media: {
78
+ payload: string;
79
+ };
80
+ }
81
+ interface TwilioOutboundMark {
82
+ event: 'mark';
83
+ streamSid: string;
84
+ mark: {
85
+ name: string;
86
+ };
87
+ }
88
+ interface TwilioOutboundClear {
89
+ event: 'clear';
90
+ streamSid: string;
91
+ }
92
+ type TwilioOutboundMessage = TwilioOutboundMedia | TwilioOutboundMark | TwilioOutboundClear;
93
+ /**
94
+ * Parse a raw WebSocket frame into a Twilio inbound message.
95
+ * Returns null for non-JSON frames or unknown event types (Twilio may add
96
+ * events; unknown ones must not crash a live call).
97
+ */
98
+ declare function parseTwilioMessage(raw: string | Buffer): TwilioInboundMessage | null;
99
+ //#endregion
100
+ //#region src/twilio/transport.d.ts
101
+ /** Minimal ws-shaped socket. `ws`-package sockets satisfy this directly. */
102
+ interface WebSocketLike {
103
+ send(data: string): void;
104
+ close(code?: number, reason?: string): void;
105
+ on(event: 'message', listener: (data: unknown, isBinary?: boolean) => void): unknown;
106
+ on(event: 'close', listener: (code?: number, reason?: unknown) => void): unknown;
107
+ on(event: 'error', listener: (error: Error) => void): unknown;
108
+ readyState?: number;
109
+ }
110
+ interface TransportEvents extends Record<string, (...args: any[]) => void> {
111
+ start: (event: TwilioStartEvent) => void;
112
+ media: (event: TwilioMediaEvent) => void;
113
+ stop: (event: TwilioStopEvent) => void;
114
+ mark: (event: TwilioMarkEvent) => void;
115
+ dtmf: (event: TwilioDtmfEvent) => void;
116
+ close: (info: {
117
+ code?: number;
118
+ reason?: string;
119
+ }) => void;
120
+ error: (error: Error) => void;
121
+ }
122
+ interface AwaitStartOptions {
123
+ /** Max time to wait for the `start` frame. Default 5000. */
124
+ timeoutMs?: number;
125
+ /** Max non-start frames tolerated before giving up. Default 8. */
126
+ maxPreStartMessages?: number;
127
+ }
128
+ declare class TwilioMediaTransport extends TypedEmitter<TransportEvents> {
129
+ private readonly ws;
130
+ private streamSidValue;
131
+ private startEvent;
132
+ private closed;
133
+ constructor(ws: WebSocketLike);
134
+ get streamSid(): string | null;
135
+ get start(): TwilioStartEvent | null;
136
+ get isOpen(): boolean;
137
+ /**
138
+ * Resolve with the `start` frame, or reject on timeout / too many pre-start
139
+ * frames / socket close. Media frames arriving before `start` are counted
140
+ * but not delivered (there is no session to deliver them to yet).
141
+ */
142
+ awaitStart(options?: AwaitStartOptions): Promise<TwilioStartEvent>;
143
+ /** Forward one media payload (base64 μ-law) to the caller. */
144
+ sendMedia(payload: string): void;
145
+ /** Interleave a named mark; Twilio echoes it once playout reaches it. */
146
+ sendMark(name: string): void;
147
+ /** Flush Twilio's playout buffer (barge-in). Pending marks echo back as discarded. */
148
+ sendClear(): void;
149
+ close(code?: number, reason?: string): void;
150
+ private handleMessage;
151
+ private requireStreamSid;
152
+ private sendRaw;
153
+ }
154
+ //#endregion
155
+ export { TwilioConnectedEvent as a, TwilioMarkEvent as c, TwilioOutboundMark as d, TwilioOutboundMedia as f, parseTwilioMessage as g, TwilioStopEvent as h, WebSocketLike as i, TwilioMediaEvent as l, TwilioStartEvent as m, TransportEvents as n, TwilioDtmfEvent as o, TwilioOutboundMessage as p, TwilioMediaTransport as r, TwilioInboundMessage as s, AwaitStartOptions as t, TwilioOutboundClear as u };
@@ -0,0 +1,8 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rest = require("./rest-BvUKut_k.cjs");
3
+ exports.TwilioMediaTransport = require_rest.TwilioMediaTransport;
4
+ exports.TwilioRestClient = require_rest.TwilioRestClient;
5
+ exports.assertValidPhoneNumber = require_rest.assertValidPhoneNumber;
6
+ exports.connectStreamTwiml = require_rest.connectStreamTwiml;
7
+ exports.escapeXml = require_rest.escapeXml;
8
+ exports.parseTwilioMessage = require_rest.parseTwilioMessage;
@@ -0,0 +1,3 @@
1
+ import { a as TwilioConnectedEvent, c as TwilioMarkEvent, d as TwilioOutboundMark, f as TwilioOutboundMedia, g as parseTwilioMessage, h as TwilioStopEvent, i as WebSocketLike, l as TwilioMediaEvent, m as TwilioStartEvent, n as TransportEvents, o as TwilioDtmfEvent, p as TwilioOutboundMessage, r as TwilioMediaTransport, s as TwilioInboundMessage, t as AwaitStartOptions, u as TwilioOutboundClear } from "./transport-B_PJFIVd.cjs";
2
+ import { a as TwilioRestConfig, i as TwilioRestClient, n as connectStreamTwiml, o as assertValidPhoneNumber, r as escapeXml, t as ConnectStreamTwimlOptions } from "./twiml-z9LjoF4_.cjs";
3
+ export { type AwaitStartOptions, type ConnectStreamTwimlOptions, type TransportEvents, type TwilioConnectedEvent, type TwilioDtmfEvent, type TwilioInboundMessage, type TwilioMarkEvent, type TwilioMediaEvent, TwilioMediaTransport, type TwilioOutboundClear, type TwilioOutboundMark, type TwilioOutboundMedia, type TwilioOutboundMessage, TwilioRestClient, type TwilioRestConfig, type TwilioStartEvent, type TwilioStopEvent, type WebSocketLike, assertValidPhoneNumber, connectStreamTwiml, escapeXml, parseTwilioMessage };
@@ -0,0 +1,3 @@
1
+ import { a as TwilioConnectedEvent, c as TwilioMarkEvent, d as TwilioOutboundMark, f as TwilioOutboundMedia, g as parseTwilioMessage, h as TwilioStopEvent, i as WebSocketLike, l as TwilioMediaEvent, m as TwilioStartEvent, n as TransportEvents, o as TwilioDtmfEvent, p as TwilioOutboundMessage, r as TwilioMediaTransport, s as TwilioInboundMessage, t as AwaitStartOptions, u as TwilioOutboundClear } from "./transport-CEaLFV4E.mjs";
2
+ import { a as TwilioRestConfig, i as TwilioRestClient, n as connectStreamTwiml, o as assertValidPhoneNumber, r as escapeXml, t as ConnectStreamTwimlOptions } from "./twiml-z9LjoF4_.mjs";
3
+ export { type AwaitStartOptions, type ConnectStreamTwimlOptions, type TransportEvents, type TwilioConnectedEvent, type TwilioDtmfEvent, type TwilioInboundMessage, type TwilioMarkEvent, type TwilioMediaEvent, TwilioMediaTransport, type TwilioOutboundClear, type TwilioOutboundMark, type TwilioOutboundMedia, type TwilioOutboundMessage, TwilioRestClient, type TwilioRestConfig, type TwilioStartEvent, type TwilioStopEvent, type WebSocketLike, assertValidPhoneNumber, connectStreamTwiml, escapeXml, parseTwilioMessage };
@@ -0,0 +1,2 @@
1
+ import { a as TwilioMediaTransport, i as escapeXml, n as assertValidPhoneNumber, o as parseTwilioMessage, r as connectStreamTwiml, t as TwilioRestClient } from "./rest-BYqiVOhe.mjs";
2
+ export { TwilioMediaTransport, TwilioRestClient, assertValidPhoneNumber, connectStreamTwiml, escapeXml, parseTwilioMessage };
@@ -0,0 +1,67 @@
1
+ //#region src/twilio/rest.d.ts
2
+ /**
3
+ * Twilio REST control plane: hangup, transfer, SMS/WhatsApp.
4
+ *
5
+ * Media and control are different planes — the WebSocket carries audio, but
6
+ * ending or redirecting the PSTN leg goes through the REST API. The `twilio`
7
+ * SDK is an optional peer dependency, imported lazily so bridges that never
8
+ * use REST features don't need it installed.
9
+ */
10
+ interface TwilioRestConfig {
11
+ accountSid: string;
12
+ authToken: string;
13
+ callerId?: string;
14
+ }
15
+ declare function assertValidPhoneNumber(phoneNumber: string): void;
16
+ declare class TwilioRestClient {
17
+ private readonly config;
18
+ private clientPromise;
19
+ constructor(config: TwilioRestConfig);
20
+ private client;
21
+ /** End the PSTN leg cleanly (better than just dropping the stream). */
22
+ completeCall(callSid: string): Promise<void>;
23
+ /**
24
+ * Blind-transfer the call: replace its TwiML with a `<Dial>`. The number
25
+ * is validated and XML-escaped — never interpolate LLM output raw into
26
+ * markup.
27
+ */
28
+ transferCall(callSid: string, phoneNumber: string, options?: {
29
+ callerId?: string;
30
+ }): Promise<void>;
31
+ sendSms(options: {
32
+ to: string;
33
+ body: string;
34
+ from: string;
35
+ }): Promise<{
36
+ sid: string;
37
+ }>;
38
+ sendWhatsapp(options: {
39
+ to: string;
40
+ body: string;
41
+ from: string;
42
+ }): Promise<{
43
+ sid: string;
44
+ }>;
45
+ }
46
+ //#endregion
47
+ //#region src/twilio/twiml.d.ts
48
+ /** TwiML helpers for wiring a call into the media-stream bridge. */
49
+ declare function escapeXml(value: string): string;
50
+ interface ConnectStreamTwimlOptions {
51
+ /** wss:// URL of the media-stream WebSocket route. */
52
+ wsUrl: string;
53
+ /**
54
+ * Custom `<Parameter>`s delivered in the start frame's customParameters —
55
+ * the place for auth tokens, tenant ids, or per-call config keys.
56
+ */
57
+ parameters?: Record<string, string>;
58
+ }
59
+ /**
60
+ * Build the TwiML that connects a call to the bridge.
61
+ *
62
+ * `<Connect><Stream>` is the bidirectional form. `<Start><Stream>` is
63
+ * send-only — the caller would never hear the agent.
64
+ */
65
+ declare function connectStreamTwiml(options: ConnectStreamTwimlOptions): string;
66
+ //#endregion
67
+ export { TwilioRestConfig as a, TwilioRestClient as i, connectStreamTwiml as n, assertValidPhoneNumber as o, escapeXml as r, ConnectStreamTwimlOptions as t };
@@ -0,0 +1,67 @@
1
+ //#region src/twilio/rest.d.ts
2
+ /**
3
+ * Twilio REST control plane: hangup, transfer, SMS/WhatsApp.
4
+ *
5
+ * Media and control are different planes — the WebSocket carries audio, but
6
+ * ending or redirecting the PSTN leg goes through the REST API. The `twilio`
7
+ * SDK is an optional peer dependency, imported lazily so bridges that never
8
+ * use REST features don't need it installed.
9
+ */
10
+ interface TwilioRestConfig {
11
+ accountSid: string;
12
+ authToken: string;
13
+ callerId?: string;
14
+ }
15
+ declare function assertValidPhoneNumber(phoneNumber: string): void;
16
+ declare class TwilioRestClient {
17
+ private readonly config;
18
+ private clientPromise;
19
+ constructor(config: TwilioRestConfig);
20
+ private client;
21
+ /** End the PSTN leg cleanly (better than just dropping the stream). */
22
+ completeCall(callSid: string): Promise<void>;
23
+ /**
24
+ * Blind-transfer the call: replace its TwiML with a `<Dial>`. The number
25
+ * is validated and XML-escaped — never interpolate LLM output raw into
26
+ * markup.
27
+ */
28
+ transferCall(callSid: string, phoneNumber: string, options?: {
29
+ callerId?: string;
30
+ }): Promise<void>;
31
+ sendSms(options: {
32
+ to: string;
33
+ body: string;
34
+ from: string;
35
+ }): Promise<{
36
+ sid: string;
37
+ }>;
38
+ sendWhatsapp(options: {
39
+ to: string;
40
+ body: string;
41
+ from: string;
42
+ }): Promise<{
43
+ sid: string;
44
+ }>;
45
+ }
46
+ //#endregion
47
+ //#region src/twilio/twiml.d.ts
48
+ /** TwiML helpers for wiring a call into the media-stream bridge. */
49
+ declare function escapeXml(value: string): string;
50
+ interface ConnectStreamTwimlOptions {
51
+ /** wss:// URL of the media-stream WebSocket route. */
52
+ wsUrl: string;
53
+ /**
54
+ * Custom `<Parameter>`s delivered in the start frame's customParameters —
55
+ * the place for auth tokens, tenant ids, or per-call config keys.
56
+ */
57
+ parameters?: Record<string, string>;
58
+ }
59
+ /**
60
+ * Build the TwiML that connects a call to the bridge.
61
+ *
62
+ * `<Connect><Stream>` is the bidirectional form. `<Start><Stream>` is
63
+ * send-only — the caller would never hear the agent.
64
+ */
65
+ declare function connectStreamTwiml(options: ConnectStreamTwimlOptions): string;
66
+ //#endregion
67
+ export { TwilioRestConfig as a, TwilioRestClient as i, connectStreamTwiml as n, assertValidPhoneNumber as o, escapeXml as r, ConnectStreamTwimlOptions as t };
package/dist/xai.cjs ADDED
@@ -0,0 +1,92 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_BaseRealtimeProvider = require("./BaseRealtimeProvider-DI4pKtOb.cjs");
3
+ const require_session_config = require("./session-config-c8sOw1XL.cjs");
4
+ const require_env = require("./env-DSnGaERV.cjs");
5
+ const require_OpenAICompatibleProvider = require("./OpenAICompatibleProvider-NS4cKVQj.cjs");
6
+ //#region src/xai.ts
7
+ /**
8
+ * xAI Grok Voice Agent provider — `realtime-voice-agents/xai`.
9
+ *
10
+ * The Voice Agent API is OpenAI-Realtime-compatible on the wire (GA event
11
+ * names, `audio/pcmu` at 8 kHz supported → zero transcoding), with a slightly
12
+ * different session shape: `voice` and `turn_detection` live at the session
13
+ * root, audio formats carry an explicit `rate`, and transcription is
14
+ * configured via `language_hint`/`keyterms`.
15
+ * Docs: https://docs.x.ai/developers/model-capabilities/audio/voice-agent
16
+ */
17
+ const XAI_BASE_URL = "wss://api.x.ai/v1/realtime";
18
+ /** Env vars checked (in order) when no explicit apiKey is passed. */
19
+ const XAI_KEY_ENV_VARS = [
20
+ "XAI_API_KEY",
21
+ "GROK_API_KEY",
22
+ "XAI_KEY"
23
+ ];
24
+ /** Alias tracking xAI's flagship voice model. */
25
+ const XAI_DEFAULT_MODEL = "grok-voice-latest";
26
+ const XAI_DEFAULT_VOICE = "eve";
27
+ /** xAI session.update: GA-style nested audio, voice/turn_detection at root. */
28
+ function buildXaiSessionUpdate(init, config = {}) {
29
+ const voice = init.voice ?? config.defaultVoice;
30
+ const transcription = init.transcription === false ? void 0 : init.transcription;
31
+ let session = {
32
+ instructions: init.instructions,
33
+ ...voice ? { voice } : {},
34
+ turn_detection: require_session_config.buildTurnDetection(init.vad),
35
+ tools: (init.tools ?? []).map((tool) => ({
36
+ type: "function",
37
+ name: tool.name,
38
+ description: tool.description ?? "",
39
+ parameters: tool.parameters
40
+ })),
41
+ audio: {
42
+ input: {
43
+ format: {
44
+ type: "audio/pcmu",
45
+ rate: 8e3
46
+ },
47
+ ...transcription ? { transcription: { ...transcription.language ? { language_hint: transcription.language } : {} } } : {}
48
+ },
49
+ output: { format: {
50
+ type: "audio/pcmu",
51
+ rate: 8e3
52
+ } }
53
+ }
54
+ };
55
+ session = require_BaseRealtimeProvider.deepMerge(session, init.providerOptions);
56
+ session = require_BaseRealtimeProvider.deepMerge(session, config.extraSessionOptions);
57
+ return {
58
+ type: "session.update",
59
+ session
60
+ };
61
+ }
62
+ /** Create an xAI Grok Voice provider factory for the bridge. */
63
+ function xaiRealtime(options = {}) {
64
+ const apiKey = require_env.resolveApiKey(options.apiKey, XAI_KEY_ENV_VARS);
65
+ if (!apiKey) throw new Error(`xaiRealtime: apiKey missing (pass apiKey or set one of ${XAI_KEY_ENV_VARS.join("/")})`);
66
+ const transcription = options.transcription === false ? false : options.transcription ? { language: options.transcription.languageHint } : void 0;
67
+ const config = {
68
+ apiKey,
69
+ model: options.model ?? "grok-voice-latest",
70
+ voice: options.voice ?? "eve",
71
+ baseUrl: options.baseUrl ?? "wss://api.x.ai/v1/realtime",
72
+ defaultVad: options.vad,
73
+ defaultTranscription: transcription,
74
+ headers: options.headers,
75
+ extraSessionOptions: options.transcription ? require_BaseRealtimeProvider.deepMerge({ audio: { input: { transcription: { ...options.transcription.keyterms ? { keyterms: options.transcription.keyterms } : {} } } } }, options.sessionOptions) : options.sessionOptions,
76
+ connectTimeoutMs: options.connectTimeoutMs,
77
+ providerName: "xai",
78
+ buildSession: buildXaiSessionUpdate,
79
+ capabilityOverrides: {
80
+ truncate: false,
81
+ vadInterruptControl: false
82
+ }
83
+ };
84
+ return ({ logger }) => new require_OpenAICompatibleProvider.OpenAICompatibleProvider(config, logger);
85
+ }
86
+ //#endregion
87
+ exports.XAI_BASE_URL = XAI_BASE_URL;
88
+ exports.XAI_DEFAULT_MODEL = XAI_DEFAULT_MODEL;
89
+ exports.XAI_DEFAULT_VOICE = XAI_DEFAULT_VOICE;
90
+ exports.XAI_KEY_ENV_VARS = XAI_KEY_ENV_VARS;
91
+ exports.buildXaiSessionUpdate = buildXaiSessionUpdate;
92
+ exports.xaiRealtime = xaiRealtime;
package/dist/xai.d.cts ADDED
@@ -0,0 +1,35 @@
1
+ import { c as VadConfig, i as ProviderSessionInit, n as ProviderFactory } from "./BaseRealtimeProvider-BehPNT1r.cjs";
2
+ //#region src/xai.d.ts
3
+ declare const XAI_BASE_URL = "wss://api.x.ai/v1/realtime";
4
+ /** Env vars checked (in order) when no explicit apiKey is passed. */
5
+ declare const XAI_KEY_ENV_VARS: readonly ["XAI_API_KEY", "GROK_API_KEY", "XAI_KEY"];
6
+ /** Alias tracking xAI's flagship voice model. */
7
+ declare const XAI_DEFAULT_MODEL = "grok-voice-latest";
8
+ declare const XAI_DEFAULT_VOICE = "eve";
9
+ interface XaiRealtimeOptions {
10
+ /** Defaults to the first of XAI_API_KEY / GROK_API_KEY / XAI_KEY set in the env. */
11
+ apiKey?: string;
12
+ /** `grok-voice-latest` (default), `grok-voice-think-fast-2.0`, … */
13
+ model?: string;
14
+ voice?: string;
15
+ baseUrl?: string;
16
+ vad?: VadConfig | null;
17
+ /** Speech-recognition hints. `false` omits transcription config. */
18
+ transcription?: {
19
+ languageHint?: string;
20
+ keyterms?: string[];
21
+ } | false;
22
+ headers?: Record<string, string>;
23
+ /** Provider-native session fields, deep-merged last (escape hatch). */
24
+ sessionOptions?: Record<string, unknown>;
25
+ connectTimeoutMs?: number;
26
+ }
27
+ /** xAI session.update: GA-style nested audio, voice/turn_detection at root. */
28
+ declare function buildXaiSessionUpdate(init: ProviderSessionInit, config?: {
29
+ defaultVoice?: string;
30
+ extraSessionOptions?: Record<string, unknown>;
31
+ }): Record<string, unknown>;
32
+ /** Create an xAI Grok Voice provider factory for the bridge. */
33
+ declare function xaiRealtime(options?: XaiRealtimeOptions): ProviderFactory;
34
+ //#endregion
35
+ export { XAI_BASE_URL, XAI_DEFAULT_MODEL, XAI_DEFAULT_VOICE, XAI_KEY_ENV_VARS, XaiRealtimeOptions, buildXaiSessionUpdate, xaiRealtime };
package/dist/xai.d.mts ADDED
@@ -0,0 +1,35 @@
1
+ import { c as VadConfig, i as ProviderSessionInit, n as ProviderFactory } from "./BaseRealtimeProvider-ClP8Wx1X.mjs";
2
+ //#region src/xai.d.ts
3
+ declare const XAI_BASE_URL = "wss://api.x.ai/v1/realtime";
4
+ /** Env vars checked (in order) when no explicit apiKey is passed. */
5
+ declare const XAI_KEY_ENV_VARS: readonly ["XAI_API_KEY", "GROK_API_KEY", "XAI_KEY"];
6
+ /** Alias tracking xAI's flagship voice model. */
7
+ declare const XAI_DEFAULT_MODEL = "grok-voice-latest";
8
+ declare const XAI_DEFAULT_VOICE = "eve";
9
+ interface XaiRealtimeOptions {
10
+ /** Defaults to the first of XAI_API_KEY / GROK_API_KEY / XAI_KEY set in the env. */
11
+ apiKey?: string;
12
+ /** `grok-voice-latest` (default), `grok-voice-think-fast-2.0`, … */
13
+ model?: string;
14
+ voice?: string;
15
+ baseUrl?: string;
16
+ vad?: VadConfig | null;
17
+ /** Speech-recognition hints. `false` omits transcription config. */
18
+ transcription?: {
19
+ languageHint?: string;
20
+ keyterms?: string[];
21
+ } | false;
22
+ headers?: Record<string, string>;
23
+ /** Provider-native session fields, deep-merged last (escape hatch). */
24
+ sessionOptions?: Record<string, unknown>;
25
+ connectTimeoutMs?: number;
26
+ }
27
+ /** xAI session.update: GA-style nested audio, voice/turn_detection at root. */
28
+ declare function buildXaiSessionUpdate(init: ProviderSessionInit, config?: {
29
+ defaultVoice?: string;
30
+ extraSessionOptions?: Record<string, unknown>;
31
+ }): Record<string, unknown>;
32
+ /** Create an xAI Grok Voice provider factory for the bridge. */
33
+ declare function xaiRealtime(options?: XaiRealtimeOptions): ProviderFactory;
34
+ //#endregion
35
+ export { XAI_BASE_URL, XAI_DEFAULT_MODEL, XAI_DEFAULT_VOICE, XAI_KEY_ENV_VARS, XaiRealtimeOptions, buildXaiSessionUpdate, xaiRealtime };
package/dist/xai.mjs ADDED
@@ -0,0 +1,86 @@
1
+ import { n as deepMerge } from "./BaseRealtimeProvider-BQigr5mB.mjs";
2
+ import { n as buildTurnDetection } from "./session-config-BVLl7-ha.mjs";
3
+ import { t as resolveApiKey } from "./env-DUwUWTsg.mjs";
4
+ import { t as OpenAICompatibleProvider } from "./OpenAICompatibleProvider-Bdtl-UXH.mjs";
5
+ //#region src/xai.ts
6
+ /**
7
+ * xAI Grok Voice Agent provider — `realtime-voice-agents/xai`.
8
+ *
9
+ * The Voice Agent API is OpenAI-Realtime-compatible on the wire (GA event
10
+ * names, `audio/pcmu` at 8 kHz supported → zero transcoding), with a slightly
11
+ * different session shape: `voice` and `turn_detection` live at the session
12
+ * root, audio formats carry an explicit `rate`, and transcription is
13
+ * configured via `language_hint`/`keyterms`.
14
+ * Docs: https://docs.x.ai/developers/model-capabilities/audio/voice-agent
15
+ */
16
+ const XAI_BASE_URL = "wss://api.x.ai/v1/realtime";
17
+ /** Env vars checked (in order) when no explicit apiKey is passed. */
18
+ const XAI_KEY_ENV_VARS = [
19
+ "XAI_API_KEY",
20
+ "GROK_API_KEY",
21
+ "XAI_KEY"
22
+ ];
23
+ /** Alias tracking xAI's flagship voice model. */
24
+ const XAI_DEFAULT_MODEL = "grok-voice-latest";
25
+ const XAI_DEFAULT_VOICE = "eve";
26
+ /** xAI session.update: GA-style nested audio, voice/turn_detection at root. */
27
+ function buildXaiSessionUpdate(init, config = {}) {
28
+ const voice = init.voice ?? config.defaultVoice;
29
+ const transcription = init.transcription === false ? void 0 : init.transcription;
30
+ let session = {
31
+ instructions: init.instructions,
32
+ ...voice ? { voice } : {},
33
+ turn_detection: buildTurnDetection(init.vad),
34
+ tools: (init.tools ?? []).map((tool) => ({
35
+ type: "function",
36
+ name: tool.name,
37
+ description: tool.description ?? "",
38
+ parameters: tool.parameters
39
+ })),
40
+ audio: {
41
+ input: {
42
+ format: {
43
+ type: "audio/pcmu",
44
+ rate: 8e3
45
+ },
46
+ ...transcription ? { transcription: { ...transcription.language ? { language_hint: transcription.language } : {} } } : {}
47
+ },
48
+ output: { format: {
49
+ type: "audio/pcmu",
50
+ rate: 8e3
51
+ } }
52
+ }
53
+ };
54
+ session = deepMerge(session, init.providerOptions);
55
+ session = deepMerge(session, config.extraSessionOptions);
56
+ return {
57
+ type: "session.update",
58
+ session
59
+ };
60
+ }
61
+ /** Create an xAI Grok Voice provider factory for the bridge. */
62
+ function xaiRealtime(options = {}) {
63
+ const apiKey = resolveApiKey(options.apiKey, XAI_KEY_ENV_VARS);
64
+ if (!apiKey) throw new Error(`xaiRealtime: apiKey missing (pass apiKey or set one of ${XAI_KEY_ENV_VARS.join("/")})`);
65
+ const transcription = options.transcription === false ? false : options.transcription ? { language: options.transcription.languageHint } : void 0;
66
+ const config = {
67
+ apiKey,
68
+ model: options.model ?? "grok-voice-latest",
69
+ voice: options.voice ?? "eve",
70
+ baseUrl: options.baseUrl ?? "wss://api.x.ai/v1/realtime",
71
+ defaultVad: options.vad,
72
+ defaultTranscription: transcription,
73
+ headers: options.headers,
74
+ extraSessionOptions: options.transcription ? deepMerge({ audio: { input: { transcription: { ...options.transcription.keyterms ? { keyterms: options.transcription.keyterms } : {} } } } }, options.sessionOptions) : options.sessionOptions,
75
+ connectTimeoutMs: options.connectTimeoutMs,
76
+ providerName: "xai",
77
+ buildSession: buildXaiSessionUpdate,
78
+ capabilityOverrides: {
79
+ truncate: false,
80
+ vadInterruptControl: false
81
+ }
82
+ };
83
+ return ({ logger }) => new OpenAICompatibleProvider(config, logger);
84
+ }
85
+ //#endregion
86
+ export { XAI_BASE_URL, XAI_DEFAULT_MODEL, XAI_DEFAULT_VOICE, XAI_KEY_ENV_VARS, buildXaiSessionUpdate, xaiRealtime };