@juspay/neurolink 9.69.3 → 9.70.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 (39) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/browser/neurolink.min.js +295 -295
  3. package/dist/lib/types/index.d.ts +1 -0
  4. package/dist/lib/types/index.js +1 -0
  5. package/dist/lib/types/livekit.d.ts +369 -0
  6. package/dist/lib/types/livekit.js +13 -0
  7. package/dist/lib/voice/livekit/brain.d.ts +21 -0
  8. package/dist/lib/voice/livekit/brain.js +75 -0
  9. package/dist/lib/voice/livekit/config.d.ts +41 -0
  10. package/dist/lib/voice/livekit/config.js +80 -0
  11. package/dist/lib/voice/livekit/eventBridge.d.ts +27 -0
  12. package/dist/lib/voice/livekit/eventBridge.js +360 -0
  13. package/dist/lib/voice/livekit/index.d.ts +15 -0
  14. package/dist/lib/voice/livekit/index.js +16 -0
  15. package/dist/lib/voice/livekit/tokens.d.ts +19 -0
  16. package/dist/lib/voice/livekit/tokens.js +51 -0
  17. package/dist/lib/voice/livekit/voiceAgent.d.ts +32 -0
  18. package/dist/lib/voice/livekit/voiceAgent.js +415 -0
  19. package/dist/lib/voice/livekit/voiceAgentWorker.d.ts +27 -0
  20. package/dist/lib/voice/livekit/voiceAgentWorker.js +58 -0
  21. package/dist/types/index.d.ts +1 -0
  22. package/dist/types/index.js +1 -0
  23. package/dist/types/livekit.d.ts +369 -0
  24. package/dist/types/livekit.js +12 -0
  25. package/dist/voice/livekit/brain.d.ts +21 -0
  26. package/dist/voice/livekit/brain.js +74 -0
  27. package/dist/voice/livekit/config.d.ts +41 -0
  28. package/dist/voice/livekit/config.js +79 -0
  29. package/dist/voice/livekit/eventBridge.d.ts +27 -0
  30. package/dist/voice/livekit/eventBridge.js +359 -0
  31. package/dist/voice/livekit/index.d.ts +15 -0
  32. package/dist/voice/livekit/index.js +15 -0
  33. package/dist/voice/livekit/tokens.d.ts +19 -0
  34. package/dist/voice/livekit/tokens.js +50 -0
  35. package/dist/voice/livekit/voiceAgent.d.ts +32 -0
  36. package/dist/voice/livekit/voiceAgent.js +414 -0
  37. package/dist/voice/livekit/voiceAgentWorker.d.ts +27 -0
  38. package/dist/voice/livekit/voiceAgentWorker.js +57 -0
  39. package/package.json +20 -6
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Types for the LiveKit voice agent integration.
3
+ *
4
+ * The integration uses LiveKit (WebRTC transport, VAD, turn detection,
5
+ * interruption, worker-per-call scaling) as the real-time loop, and NeuroLink
6
+ * as the brain (LLM, tools, memory). These types describe the brain seam, the
7
+ * worker configuration, and the join-token request — all transport-agnostic
8
+ * except where a LiveKit-specific concept is named explicitly.
9
+ *
10
+ * See docs/features/livekit-voice-agent.md.
11
+ */
12
+ import type { NeuroLinkEvents, TypedEventEmitter } from "./common.js";
13
+ import type { StreamOptions, StreamResult } from "./stream.js";
14
+ /**
15
+ * Minimal structural shape of the NeuroLink instance the brain depends on.
16
+ *
17
+ * Declared structurally (rather than importing the `NeuroLink` class) so the
18
+ * brain layer stays decoupled from SDK construction and can be unit-tested with
19
+ * a lightweight stub. The real `NeuroLink` instance satisfies this shape.
20
+ *
21
+ * `getEventEmitter` is optional so lightweight stubs remain valid; the real
22
+ * `NeuroLink` instance provides it, and the data-channel event bridge uses it
23
+ * to forward tool/text/HITL events to the browser.
24
+ */
25
+ export type LiveKitNeuroLinkStreamer = {
26
+ stream: (options: StreamOptions) => Promise<StreamResult>;
27
+ getEventEmitter?: () => TypedEventEmitter<NeuroLinkEvents>;
28
+ };
29
+ /**
30
+ * Configuration for the transport-agnostic voice brain.
31
+ *
32
+ * The brain owns the conversation: it calls `neurolink.stream()` with a stable
33
+ * `conversationId` so NeuroLink's memory layer is the source of truth, and it
34
+ * leaves tool-calling to the NeuroLink instance.
35
+ */
36
+ export type LiveKitBrainConfig = {
37
+ /** Configured NeuroLink instance (memory + tools registered on it). */
38
+ neurolink: LiveKitNeuroLinkStreamer;
39
+ /** LLM provider name passed to `stream()` (e.g. "bedrock"). */
40
+ provider?: string;
41
+ /** LLM model name passed to `stream()` (e.g. "claude-sonnet-4-6"). */
42
+ model?: string;
43
+ /** System prompt applied to every turn. */
44
+ systemPrompt?: string;
45
+ /** Sampling temperature for spoken-style responses. */
46
+ temperature?: number;
47
+ /** Upper bound on tokens per turn. */
48
+ maxTokens?: number;
49
+ /** Optional user identifier recorded alongside memory. */
50
+ userId?: string;
51
+ };
52
+ /** A single user turn handed to the brain. */
53
+ export type LiveKitBrainTurn = {
54
+ /** Final transcript of the user's utterance. */
55
+ transcript: string;
56
+ /** Stable conversation id keying NeuroLink memory for this session. */
57
+ conversationId: string;
58
+ /** Cancellation signal; aborting stops the in-flight LLM and tool calls. */
59
+ signal?: AbortSignal;
60
+ };
61
+ /**
62
+ * The brain's public surface: stream the assistant reply as text deltas.
63
+ * The transport layer converts these deltas into audio (TTS).
64
+ */
65
+ export type LiveKitVoiceBrain = {
66
+ streamReply: (turn: LiveKitBrainTurn) => AsyncGenerator<string, void, unknown>;
67
+ };
68
+ /** Speech-to-text plugin selection for the LiveKit worker. */
69
+ export type LiveKitSttConfig = {
70
+ provider: string;
71
+ model?: string;
72
+ language?: string;
73
+ /**
74
+ * Soniox only: maximum delay (ms) between speech cessation and the STT
75
+ * endpoint. Raise it so Soniox does not finalize on short pauses — that lets
76
+ * VAD silence (not the STT endpoint) decide when the turn ends.
77
+ */
78
+ maxEndpointDelayMs?: number;
79
+ };
80
+ /** Text-to-speech plugin selection for the LiveKit worker. */
81
+ export type LiveKitTtsConfig = {
82
+ provider: string;
83
+ voice?: string;
84
+ model?: string;
85
+ };
86
+ /**
87
+ * Silero VAD tuning. Stricter values reject background noise (higher threshold,
88
+ * longer minimum speech). Durations are in seconds.
89
+ */
90
+ export type LiveKitVadConfig = {
91
+ /** Probability cutoff for "this is speech" (default 0.6). Higher = stricter. */
92
+ activationThreshold?: number;
93
+ /** Minimum speech length before a turn starts, seconds (default 0.2). */
94
+ minSpeechDuration?: number;
95
+ /** Silence before a turn ends, seconds (default 0.6) — tolerates pauses. */
96
+ minSilenceDuration?: number;
97
+ };
98
+ /**
99
+ * Turn-detection (end-of-utterance) tuning.
100
+ *
101
+ * `mode` selects what decides the user's turn is over:
102
+ * - `"vad"` — Silero VAD silence (see `LiveKitVadConfig.minSilenceDuration`).
103
+ * Tolerates natural mid-sentence pauses; the turn only ends after a full
104
+ * silence window. This mirrors Clairvoyance's behavior.
105
+ * - `"stt"` — the STT provider's own endpoint detection (e.g. Soniox). Often
106
+ * much faster/aggressive — short pauses can prematurely split one utterance
107
+ * into several turns.
108
+ * - `"realtime_llm"` / `"manual"` — advanced/manual strategies.
109
+ *
110
+ * `minEndpointingDelay` / `maxEndpointingDelay` are the framework's endpointing
111
+ * window in milliseconds (in VAD mode the effective end delay is
112
+ * `max(VAD silence, minEndpointingDelay)`).
113
+ */
114
+ export type LiveKitTurnConfig = {
115
+ mode?: "stt" | "vad" | "realtime_llm" | "manual";
116
+ minEndpointingDelay?: number;
117
+ maxEndpointingDelay?: number;
118
+ };
119
+ /**
120
+ * Interruption (barge-in) tuning. Requiring real words / a minimum duration
121
+ * stops background noise from cutting off the assistant.
122
+ */
123
+ export type LiveKitInterruptionConfig = {
124
+ /** Minimum recognized words to count as an interruption (default 2). */
125
+ minWords?: number;
126
+ /** Minimum audio duration to count as an interruption, ms (default 600). */
127
+ minDuration?: number;
128
+ };
129
+ /**
130
+ * Options for `defineVoiceAgent` — the agent definition placed as the default
131
+ * export of the worker entry file.
132
+ *
133
+ * LiveKit runs each call as a Job in its own child process and re-imports the
134
+ * entry file there, so the NeuroLink instance cannot be passed as a live object
135
+ * from a parent. Instead, `createNeuroLink` is invoked **inside each job
136
+ * process** to build the brain (and register its tools) for that call.
137
+ */
138
+ export type LiveKitVoiceAgentConfig = {
139
+ /**
140
+ * Factory that builds the NeuroLink instance for a job process.
141
+ * Called once per call, inside the job's own process.
142
+ */
143
+ createNeuroLink: () => LiveKitNeuroLinkStreamer | Promise<LiveKitNeuroLinkStreamer>;
144
+ /** Realtime speech-to-text selection. */
145
+ stt: LiveKitSttConfig;
146
+ /** Realtime text-to-speech selection. */
147
+ tts: LiveKitTtsConfig;
148
+ /** LLM provider/model overrides (default to env-resolved values). */
149
+ provider?: string;
150
+ model?: string;
151
+ systemPrompt?: string;
152
+ temperature?: number;
153
+ maxTokens?: number;
154
+ /** Prefix used when deriving a per-room conversation id (default "voice"). */
155
+ conversationIdPrefix?: string;
156
+ /** Optional user id recorded alongside memory. */
157
+ userId?: string;
158
+ /** Silero VAD tuning (stricter = ignores background noise). */
159
+ vad?: LiveKitVadConfig;
160
+ /** Turn-detection tuning (VAD vs STT endpointing, delays). */
161
+ turn?: LiveKitTurnConfig;
162
+ /** Interruption tuning (require words/duration so noise can't barge in). */
163
+ interruption?: LiveKitInterruptionConfig;
164
+ /**
165
+ * Data-channel event bridge: forward NeuroLink events (text, tool calls,
166
+ * tool results, HITL prompts, status) to the browser over the LiveKit data
167
+ * channel, and accept control messages (HITL responses) back. Disabled
168
+ * unless `enabled` is `true`.
169
+ */
170
+ events?: LiveKitEventBridgeConfig;
171
+ };
172
+ /** Options for `startVoiceAgentWorker` — launches the LiveKit Agents worker. */
173
+ export type LiveKitWorkerLaunchOptions = {
174
+ /**
175
+ * Absolute path to the entry file whose default export is the result of
176
+ * `defineVoiceAgent`. LiveKit re-imports this file in each job process.
177
+ */
178
+ agentFile: string;
179
+ /** Name the worker registers under for dispatch (default "neurolink-voice"). */
180
+ agentName?: string;
181
+ };
182
+ /** Resolved LiveKit server connection settings. */
183
+ export type LiveKitServerConfig = {
184
+ /** LiveKit server URL (ws/wss). */
185
+ url: string;
186
+ /** API key for token signing and worker registration. */
187
+ apiKey: string;
188
+ /** API secret for token signing and worker registration. */
189
+ apiSecret: string;
190
+ };
191
+ /** LLM defaults resolved from the environment for the brain. */
192
+ export type LiveKitBrainDefaults = {
193
+ provider: string;
194
+ model: string;
195
+ };
196
+ /** Arguments for minting a browser join token. */
197
+ export type LiveKitTokenRequest = {
198
+ /** Participant identity (e.g. the authenticated user id). */
199
+ identity: string;
200
+ /** Room name to join (auto-created on first join). */
201
+ room: string;
202
+ /** LiveKit API key. */
203
+ apiKey: string;
204
+ /** LiveKit API secret. */
205
+ apiSecret: string;
206
+ /** Token lifetime in seconds (default 600; clamped to a 3600 max). */
207
+ ttlSeconds?: number;
208
+ };
209
+ /** Discriminant tags for outbound voice events. */
210
+ export type LiveKitVoiceEventType = "user-text" | "text" | "tool-start" | "tool-result" | "status" | "hitl-prompt" | "done";
211
+ /**
212
+ * A user STT transcript for display. Interim partials stream with
213
+ * `final: false`; the end-of-utterance result has `final: true`. The client
214
+ * updates one live bubble and commits it on `final`.
215
+ *
216
+ * `replacesPrevious` is set on the committed (`final: true`) text of a turn that
217
+ * absorbed a previous turn the user interrupted before it produced any reply
218
+ * (strict barge-in club). The client removes the orphaned previous user bubble
219
+ * so the merged utterance shows as one bubble.
220
+ */
221
+ export type LiveKitVoiceUserTextEvent = {
222
+ type: "user-text";
223
+ data: {
224
+ text: string;
225
+ final: boolean;
226
+ replacesPrevious?: boolean;
227
+ };
228
+ };
229
+ /** A streamed chunk of the assistant's spoken/written reply. */
230
+ export type LiveKitVoiceTextEvent = {
231
+ type: "text";
232
+ data: {
233
+ delta: string;
234
+ };
235
+ };
236
+ /** A tool invocation has started (best-effort status; may not always fire). */
237
+ export type LiveKitVoiceToolStartEvent = {
238
+ type: "tool-start";
239
+ data: {
240
+ id?: string;
241
+ name: string;
242
+ input?: unknown;
243
+ };
244
+ };
245
+ /**
246
+ * A tool invocation has finished. `result` carries the tool's structured
247
+ * output (for example, a chart payload) for the client to render.
248
+ */
249
+ export type LiveKitVoiceToolResultEvent = {
250
+ type: "tool-result";
251
+ data: {
252
+ id?: string;
253
+ name: string;
254
+ result?: unknown;
255
+ success?: boolean;
256
+ error?: string;
257
+ };
258
+ };
259
+ /** Coarse agent state, useful for UI indicators (e.g. "thinking…"). */
260
+ export type LiveKitVoiceStatusEvent = {
261
+ type: "status";
262
+ data: {
263
+ state: "thinking" | "speaking" | "listening" | "error";
264
+ detail?: string;
265
+ };
266
+ };
267
+ /** A human-in-the-loop confirmation the user must approve or reject. */
268
+ export type LiveKitVoiceHitlPromptEvent = {
269
+ type: "hitl-prompt";
270
+ data: {
271
+ confirmationId: string;
272
+ toolName: string;
273
+ actionType?: string;
274
+ arguments?: unknown;
275
+ timeoutMs?: number;
276
+ allowModification?: boolean;
277
+ };
278
+ };
279
+ /** The current turn has finished. */
280
+ export type LiveKitVoiceDoneEvent = {
281
+ type: "done";
282
+ data: {
283
+ reason?: string;
284
+ };
285
+ };
286
+ /** Discriminated union of all outbound voice events (before enveloping). */
287
+ export type LiveKitVoiceEvent = LiveKitVoiceUserTextEvent | LiveKitVoiceTextEvent | LiveKitVoiceToolStartEvent | LiveKitVoiceToolResultEvent | LiveKitVoiceStatusEvent | LiveKitVoiceHitlPromptEvent | LiveKitVoiceDoneEvent;
288
+ /**
289
+ * Wire format published to the browser: a `LiveKitVoiceEvent` plus a monotonic
290
+ * sequence number and a timestamp so the client can order and de-duplicate.
291
+ */
292
+ export type LiveKitVoiceEventEnvelope = LiveKitVoiceEvent & {
293
+ seq: number;
294
+ ts: number;
295
+ };
296
+ /** Control messages sent from the browser back to the agent. */
297
+ export type LiveKitVoiceControlMessage = {
298
+ action: "hitl:accept";
299
+ confirmationId: string;
300
+ modifiedArguments?: unknown;
301
+ } | {
302
+ action: "hitl:reject";
303
+ confirmationId: string;
304
+ reason?: string;
305
+ };
306
+ /**
307
+ * Configuration for the data-channel event bridge, set on
308
+ * `LiveKitVoiceAgentConfig.events`.
309
+ */
310
+ export type LiveKitEventBridgeConfig = {
311
+ /** Master switch — the bridge is inert unless this is `true`. */
312
+ enabled?: boolean;
313
+ /** Data-channel topic for outbound events (default "ai-events"). */
314
+ eventsTopic?: string;
315
+ /** Data-channel topic for inbound control messages (default "ai-control"). */
316
+ controlTopic?: string;
317
+ /** If set, only these event types are forwarded (default: all). */
318
+ include?: LiveKitVoiceEventType[];
319
+ /**
320
+ * Payloads encoded larger than this many bytes are sent via the chunked text
321
+ * stream API instead of a single reliable data packet (default 12000).
322
+ */
323
+ maxInlineBytes?: number;
324
+ };
325
+ /**
326
+ * Minimal structural view of the LiveKit room the bridge needs: a local
327
+ * participant to publish on, and event (un)subscription. Declared structurally
328
+ * so `src/lib/types` carries no dependency on `@livekit/rtc-node`; the real
329
+ * `Room` from a job context satisfies this shape.
330
+ */
331
+ export type LiveKitBridgeRoom = {
332
+ localParticipant?: {
333
+ publishData(data: Uint8Array, options: {
334
+ reliable?: boolean;
335
+ topic?: string;
336
+ }): Promise<void>;
337
+ sendText(text: string, options?: {
338
+ topic?: string;
339
+ }): Promise<unknown>;
340
+ };
341
+ on(event: string, listener: (...args: unknown[]) => void): unknown;
342
+ off(event: string, listener: (...args: unknown[]) => void): unknown;
343
+ };
344
+ /**
345
+ * Normalized tool fields extracted from a `tool:start` / `tool:end` emitter
346
+ * payload, used internally by the event bridge.
347
+ */
348
+ export type LiveKitToolEventFields = {
349
+ name: string;
350
+ id?: string;
351
+ input?: unknown;
352
+ result?: unknown;
353
+ success?: boolean;
354
+ error?: string;
355
+ };
356
+ /** Inputs to `attachEventBridge`. */
357
+ export type LiveKitEventBridgeParams = {
358
+ /** The LiveKit room for this call (from the job context). */
359
+ room: LiveKitBridgeRoom;
360
+ /** NeuroLink's event emitter (`neurolink.getEventEmitter()`). */
361
+ emitter: TypedEventEmitter<NeuroLinkEvents>;
362
+ /** Bridge options (topics, filtering, chunking threshold). */
363
+ options?: LiveKitEventBridgeConfig;
364
+ };
365
+ /** Handle returned by `attachEventBridge` for teardown. */
366
+ export type LiveKitEventBridgeHandle = {
367
+ /** Remove all listeners and stop publishing. Idempotent. */
368
+ dispose: () => void;
369
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Types for the LiveKit voice agent integration.
3
+ *
4
+ * The integration uses LiveKit (WebRTC transport, VAD, turn detection,
5
+ * interruption, worker-per-call scaling) as the real-time loop, and NeuroLink
6
+ * as the brain (LLM, tools, memory). These types describe the brain seam, the
7
+ * worker configuration, and the join-token request — all transport-agnostic
8
+ * except where a LiveKit-specific concept is named explicitly.
9
+ *
10
+ * See docs/features/livekit-voice-agent.md.
11
+ */
12
+ export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Transport-agnostic voice brain.
3
+ *
4
+ * Given a user transcript and a stable conversation id, it streams the
5
+ * assistant's reply as text deltas by calling `neurolink.stream()`. NeuroLink
6
+ * owns history (via the conversation id and its memory layer) and tools (the
7
+ * tool-calling loop runs inside `stream()`); this module never touches a
8
+ * transport. The LiveKit worker (or any other transport) consumes the text
9
+ * deltas and converts them to audio.
10
+ *
11
+ * See docs/features/livekit-voice-agent.md.
12
+ */
13
+ import type { LiveKitBrainConfig, LiveKitVoiceBrain } from "../../types/index.js";
14
+ /**
15
+ * Create a voice brain bound to a configured NeuroLink instance.
16
+ *
17
+ * The returned `streamReply` is an async generator of text deltas for a single
18
+ * turn. Aborting `turn.signal` stops the in-flight LLM and tool calls and ends
19
+ * the generator promptly (barge-in support).
20
+ */
21
+ export declare function createVoiceBrain(config: LiveKitBrainConfig): LiveKitVoiceBrain;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Transport-agnostic voice brain.
3
+ *
4
+ * Given a user transcript and a stable conversation id, it streams the
5
+ * assistant's reply as text deltas by calling `neurolink.stream()`. NeuroLink
6
+ * owns history (via the conversation id and its memory layer) and tools (the
7
+ * tool-calling loop runs inside `stream()`); this module never touches a
8
+ * transport. The LiveKit worker (or any other transport) consumes the text
9
+ * deltas and converts them to audio.
10
+ *
11
+ * See docs/features/livekit-voice-agent.md.
12
+ */
13
+ import { logger } from "../../utils/logger.js";
14
+ /**
15
+ * Extract spoken text from a stream chunk without type assertions.
16
+ *
17
+ * `StreamResult.stream` yields a union: text chunks (`{ content: string }`),
18
+ * a no-output sentinel, and audio/image events. Only text chunks carry a
19
+ * string `content`; everything else yields `undefined` and is skipped.
20
+ */
21
+ function extractTextDelta(chunk) {
22
+ if (typeof chunk === "object" && chunk !== null && "content" in chunk) {
23
+ const value = chunk.content;
24
+ if (typeof value === "string" && value.length > 0) {
25
+ return value;
26
+ }
27
+ }
28
+ return undefined;
29
+ }
30
+ /**
31
+ * Create a voice brain bound to a configured NeuroLink instance.
32
+ *
33
+ * The returned `streamReply` is an async generator of text deltas for a single
34
+ * turn. Aborting `turn.signal` stops the in-flight LLM and tool calls and ends
35
+ * the generator promptly (barge-in support).
36
+ */
37
+ export function createVoiceBrain(config) {
38
+ const { neurolink, provider, model, systemPrompt, temperature, maxTokens, userId, } = config;
39
+ async function* streamReply(turn) {
40
+ const { transcript, conversationId, signal } = turn;
41
+ if (signal?.aborted) {
42
+ return;
43
+ }
44
+ const context = {
45
+ sessionId: conversationId,
46
+ conversationId,
47
+ };
48
+ if (userId !== undefined) {
49
+ context.userId = userId;
50
+ }
51
+ const result = await neurolink.stream({
52
+ input: { text: transcript },
53
+ provider,
54
+ model,
55
+ systemPrompt,
56
+ temperature,
57
+ maxTokens,
58
+ context,
59
+ abortSignal: signal,
60
+ disableTools: false,
61
+ });
62
+ for await (const chunk of result.stream) {
63
+ if (signal?.aborted) {
64
+ logger.debug("[LiveKitBrain] Turn aborted mid-stream; stopping");
65
+ return;
66
+ }
67
+ const delta = extractTextDelta(chunk);
68
+ if (delta !== undefined) {
69
+ yield delta;
70
+ }
71
+ }
72
+ }
73
+ return { streamReply };
74
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Environment resolution for the LiveKit voice agent.
3
+ *
4
+ * Reads LiveKit connection settings and LLM defaults from `process.env` with
5
+ * descriptive errors for missing required values. No type assertions: presence
6
+ * is verified with explicit string checks.
7
+ *
8
+ * See docs/features/livekit-voice-agent.md.
9
+ */
10
+ import type { LiveKitServerConfig, LiveKitBrainDefaults } from "../../types/index.js";
11
+ /**
12
+ * Resolve LiveKit server connection settings from the environment.
13
+ *
14
+ * Requires `LIVEKIT_URL`, `LIVEKIT_API_KEY`, and `LIVEKIT_API_SECRET`. Works
15
+ * identically for LiveKit Cloud, a self-hosted server, or `livekit-server --dev`
16
+ * — only the values differ.
17
+ */
18
+ export declare function resolveLiveKitServerConfig(): LiveKitServerConfig;
19
+ /**
20
+ * Resolve the LLM provider/model defaults for the brain.
21
+ *
22
+ * Defaults to Bedrock / Claude; overridable via `VOICE_LLM_PROVIDER` and
23
+ * `VOICE_LLM_MODEL`.
24
+ */
25
+ export declare function resolveBrainDefaults(): LiveKitBrainDefaults;
26
+ /**
27
+ * Resolve the semantic end-of-utterance (EOU) turn-detection settings.
28
+ *
29
+ * Opt-in via `LIVEKIT_EOU_TURN_DETECTION` (`1`/`true`/`english`/`en`/`on`/`yes`).
30
+ * When enabled, the English `@livekit/agents-plugin-livekit` EOU model decides
31
+ * whether the user's turn is truly over, layered on top of VAD silence — so
32
+ * natural mid-sentence pauses don't split one utterance. English-only; the
33
+ * model adds ~200MB RAM per worker and ~10ms per turn-end.
34
+ *
35
+ * `LIVEKIT_EOU_UNLIKELY_THRESHOLD` optionally overrides the model's confidence
36
+ * threshold (lower = end the turn more eagerly).
37
+ */
38
+ export declare function resolveEouTurnDetection(): {
39
+ enabled: boolean;
40
+ unlikelyThreshold: number | undefined;
41
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Environment resolution for the LiveKit voice agent.
3
+ *
4
+ * Reads LiveKit connection settings and LLM defaults from `process.env` with
5
+ * descriptive errors for missing required values. No type assertions: presence
6
+ * is verified with explicit string checks.
7
+ *
8
+ * See docs/features/livekit-voice-agent.md.
9
+ */
10
+ const DEFAULT_LLM_PROVIDER = "bedrock";
11
+ const DEFAULT_LLM_MODEL = "claude-sonnet-4-6";
12
+ /** Read a required environment variable or throw a descriptive error. */
13
+ function requireEnv(name) {
14
+ const value = process.env[name];
15
+ if (typeof value !== "string" || value.trim().length === 0) {
16
+ throw new Error(`${name} is not set in environment (required for the LiveKit voice agent)`);
17
+ }
18
+ return value.trim();
19
+ }
20
+ /** Read an optional environment variable, falling back to a default. */
21
+ function readEnv(name, fallback) {
22
+ const value = process.env[name];
23
+ if (typeof value === "string" && value.trim().length > 0) {
24
+ return value.trim();
25
+ }
26
+ return fallback;
27
+ }
28
+ /**
29
+ * Resolve LiveKit server connection settings from the environment.
30
+ *
31
+ * Requires `LIVEKIT_URL`, `LIVEKIT_API_KEY`, and `LIVEKIT_API_SECRET`. Works
32
+ * identically for LiveKit Cloud, a self-hosted server, or `livekit-server --dev`
33
+ * — only the values differ.
34
+ */
35
+ export function resolveLiveKitServerConfig() {
36
+ return {
37
+ url: requireEnv("LIVEKIT_URL"),
38
+ apiKey: requireEnv("LIVEKIT_API_KEY"),
39
+ apiSecret: requireEnv("LIVEKIT_API_SECRET"),
40
+ };
41
+ }
42
+ /**
43
+ * Resolve the LLM provider/model defaults for the brain.
44
+ *
45
+ * Defaults to Bedrock / Claude; overridable via `VOICE_LLM_PROVIDER` and
46
+ * `VOICE_LLM_MODEL`.
47
+ */
48
+ export function resolveBrainDefaults() {
49
+ return {
50
+ provider: readEnv("VOICE_LLM_PROVIDER", DEFAULT_LLM_PROVIDER),
51
+ model: readEnv("VOICE_LLM_MODEL", DEFAULT_LLM_MODEL),
52
+ };
53
+ }
54
+ const EOU_TRUTHY = new Set(["1", "true", "english", "en", "on", "yes"]);
55
+ /**
56
+ * Resolve the semantic end-of-utterance (EOU) turn-detection settings.
57
+ *
58
+ * Opt-in via `LIVEKIT_EOU_TURN_DETECTION` (`1`/`true`/`english`/`en`/`on`/`yes`).
59
+ * When enabled, the English `@livekit/agents-plugin-livekit` EOU model decides
60
+ * whether the user's turn is truly over, layered on top of VAD silence — so
61
+ * natural mid-sentence pauses don't split one utterance. English-only; the
62
+ * model adds ~200MB RAM per worker and ~10ms per turn-end.
63
+ *
64
+ * `LIVEKIT_EOU_UNLIKELY_THRESHOLD` optionally overrides the model's confidence
65
+ * threshold (lower = end the turn more eagerly).
66
+ */
67
+ export function resolveEouTurnDetection() {
68
+ const raw = process.env.LIVEKIT_EOU_TURN_DETECTION;
69
+ const enabled = typeof raw === "string" && EOU_TRUTHY.has(raw.trim().toLowerCase());
70
+ const thresholdRaw = process.env.LIVEKIT_EOU_UNLIKELY_THRESHOLD;
71
+ let unlikelyThreshold;
72
+ if (typeof thresholdRaw === "string" && thresholdRaw.trim().length > 0) {
73
+ const parsed = Number(thresholdRaw.trim());
74
+ if (Number.isFinite(parsed)) {
75
+ unlikelyThreshold = parsed;
76
+ }
77
+ }
78
+ return { enabled, unlikelyThreshold };
79
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Data-channel event bridge.
3
+ *
4
+ * Connects NeuroLink's event emitter to the LiveKit room's data channel: it
5
+ * forwards NeuroLink events (text deltas, tool start/result, HITL prompts,
6
+ * stream lifecycle) to the browser as small versioned envelopes, and accepts
7
+ * control messages (HITL responses) back, re-emitting them onto the emitter so
8
+ * NeuroLink's HITL manager resolves.
9
+ *
10
+ * This is the WebRTC counterpart of the chat-mode SSE controller: same event
11
+ * source, different transport. The browser renders the envelopes (transcript,
12
+ * tool status, charts, confirmation prompts).
13
+ *
14
+ * `@livekit/rtc-node` is an optional dependency, imported dynamically only for
15
+ * the `RoomEvent` enum value. All payloads arrive typed as `unknown` and are
16
+ * narrowed with runtime guards — no type assertions.
17
+ *
18
+ * See docs/features/livekit-voice-agent.md.
19
+ */
20
+ import type { LiveKitEventBridgeHandle, LiveKitEventBridgeParams } from "../../types/index.js";
21
+ /**
22
+ * Attach the data-channel event bridge to a room.
23
+ *
24
+ * Returns a handle whose `dispose()` removes every listener and stops
25
+ * publishing; it is safe to call more than once.
26
+ */
27
+ export declare function attachEventBridge(params: LiveKitEventBridgeParams): Promise<LiveKitEventBridgeHandle>;