@mastra/voice-google-gemini-live 0.14.13 → 0.14.14

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#adapterContext","#correlate","#export","#rawConfig"],"sources":["../../../packages/_internal-core/dist/logger/index.js","../../../packages/_internal-core/dist/base/index.js","../../../packages/_internals/voice/dist/aisdk-C_K-VCwq.js","../src/managers/AudioStreamManager.ts","../src/utils/errors.ts","../src/managers/ConnectionManager.ts","../src/managers/ContextManager.ts","../src/managers/AuthManager.ts","../src/managers/EventManager.ts","../src/index.ts"],"sourcesContent":["import { Transform } from \"stream\";\n//#region src/logger/adapter.ts\nfunction isAdaptableLogger(logger) {\n\treturn typeof logger.__attachObservability === \"function\";\n}\n/**\n* Export a tracked exception through the adapter sink, mirroring the\n* DualLogger dual-write shape (`errorId`/`domain`/`category`/`details`/`cause`\n* when present on a MastraError-like value). Never throws into the caller.\n*/\nfunction exportTrackedException(ctx, error, metadata) {\n\tif (!ctx?.options.export) return;\n\ttry {\n\t\tconst mastraError = error;\n\t\tctx.getLogSink()?.error(error.message, {\n\t\t\t...mastraError.id !== void 0 ? { errorId: mastraError.id } : {},\n\t\t\t...mastraError.domain !== void 0 ? { domain: mastraError.domain } : {},\n\t\t\t...mastraError.category !== void 0 ? { category: mastraError.category } : {},\n\t\t\t...mastraError.details !== void 0 ? { details: mastraError.details } : {},\n\t\t\t...error.cause instanceof Error ? { cause: error.cause.message } : {},\n\t\t\t...metadata\n\t\t});\n\t} catch {}\n}\n/**\n* Adapt IMastraLogger's variadic args into the structured `data` payload of\n* an exported log record. Extracts the first plain object as data,\n* serializes an Error arg, and collects remaining primitives under `args`\n* so the derived record preserves all context from the native call.\n*/\nfunction buildLogRecordData(args) {\n\tconst objectData = args.find((arg) => arg !== null && typeof arg === \"object\" && !Array.isArray(arg) && !(arg instanceof Error));\n\tconst errorArg = args.find((arg) => arg instanceof Error);\n\tconst extraArgs = args.filter((arg) => arg !== objectData && arg !== errorArg);\n\tif (!objectData && !errorArg && extraArgs.length === 0) return void 0;\n\treturn {\n\t\t...objectData ?? {},\n\t\t...errorArg ? { error: {\n\t\t\tname: errorArg.name,\n\t\t\tmessage: errorArg.message,\n\t\t\tstack: errorArg.stack\n\t\t} } : {},\n\t\t...extraArgs.length > 0 ? { args: extraArgs } : {}\n\t};\n}\n//#endregion\n//#region src/logger/index.ts\nconst RegisteredLogger = {\n\tAGENT: \"AGENT\",\n\tOBSERVABILITY: \"OBSERVABILITY\",\n\tAUTH: \"AUTH\",\n\tBROWSER: \"BROWSER\",\n\tNETWORK: \"NETWORK\",\n\tWORKFLOW: \"WORKFLOW\",\n\tLLM: \"LLM\",\n\tTTS: \"TTS\",\n\tVOICE: \"VOICE\",\n\tVECTOR: \"VECTOR\",\n\tBUNDLER: \"BUNDLER\",\n\tDEPLOYER: \"DEPLOYER\",\n\tMEMORY: \"MEMORY\",\n\tSTORAGE: \"STORAGE\",\n\tEMBEDDINGS: \"EMBEDDINGS\",\n\tMCP_SERVER: \"MCP_SERVER\",\n\tSERVER_CACHE: \"SERVER_CACHE\",\n\tSERVER: \"SERVER\",\n\tWORKSPACE: \"WORKSPACE\",\n\tCHANNEL: \"CHANNEL\"\n};\nconst LogLevel = {\n\tDEBUG: \"debug\",\n\tINFO: \"info\",\n\tWARN: \"warn\",\n\tERROR: \"error\",\n\tNONE: \"silent\"\n};\nvar LoggerTransport = class extends Transform {\n\tconstructor(opts = {}) {\n\t\tsuper({\n\t\t\t...opts,\n\t\t\tobjectMode: true\n\t\t});\n\t}\n\tasync listLogsByRunId(_args) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _args?.page ?? 1,\n\t\t\tperPage: _args?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n\tasync listLogs(_args) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _args?.page ?? 1,\n\t\t\tperPage: _args?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n};\nconst createCustomTransport = (stream, listLogs, listLogsByRunId) => {\n\tlet transport = stream;\n\tif (listLogs) transport.listLogs = listLogs;\n\tif (listLogsByRunId) transport.listLogsByRunId = listLogsByRunId;\n\treturn transport;\n};\nvar MastraLogger = class {\n\tname;\n\tlevel;\n\ttransports;\n\tconstructor(options = {}) {\n\t\tthis.name = options.name || \"Mastra\";\n\t\tthis.level = options.level || LogLevel.ERROR;\n\t\tthis.transports = new Map(Object.entries(options.transports || {}));\n\t}\n\tgetTransports() {\n\t\treturn this.transports;\n\t}\n\ttrackException(_error, _metadata) {}\n\tasync listLogs(transportId, params) {\n\t\tif (!transportId || !this.transports.has(transportId)) return {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: params?.page ?? 1,\n\t\t\tperPage: params?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t\treturn this.transports.get(transportId).listLogs?.(params) ?? {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: params?.page ?? 1,\n\t\t\tperPage: params?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n\tasync listLogsByRunId({ transportId, runId, fromDate, toDate, logLevel, filters, page, perPage }) {\n\t\tif (!transportId || !this.transports.has(transportId) || !runId) return {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: page ?? 1,\n\t\t\tperPage: perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t\treturn this.transports.get(transportId).listLogsByRunId?.({\n\t\t\trunId,\n\t\t\tfromDate,\n\t\t\ttoDate,\n\t\t\tlogLevel,\n\t\t\tfilters,\n\t\t\tpage,\n\t\t\tperPage\n\t\t}) ?? {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: page ?? 1,\n\t\t\tperPage: perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n};\nvar ConsoleLogger = class ConsoleLogger extends MastraLogger {\n\tcomponent;\n\tfilter;\n\t#adapterContext;\n\tconstructor(options = {}) {\n\t\tsuper(options);\n\t\tthis.component = options.component;\n\t\tthis.filter = options.filter;\n\t}\n\t/**\n\t* Adapter hook (see `AdaptableLogger`): enables native trace correlation\n\t* (trace_id/span_id appended to console output) and observability export\n\t* derived from the same record. Called by Mastra during setup.\n\t*/\n\t__attachObservability(ctx) {\n\t\tthis.#adapterContext = ctx;\n\t}\n\tchild(componentOrBindings) {\n\t\tconst component = typeof componentOrBindings === \"string\" ? componentOrBindings : componentOrBindings?.component ?? this.component;\n\t\tconst child = new ConsoleLogger({\n\t\t\tname: this.name,\n\t\t\tlevel: this.level,\n\t\t\tcomponent,\n\t\t\tfilter: this.filter\n\t\t});\n\t\tif (this.#adapterContext) child.__attachObservability(this.#adapterContext);\n\t\treturn child;\n\t}\n\t/**\n\t* Native-record correlation: when a span is active, append the trace\n\t* fields object to the console args so every destination sees them.\n\t*/\n\t#correlate(args) {\n\t\tconst ctx = this.#adapterContext;\n\t\tif (!ctx?.options.correlation) return args;\n\t\ttry {\n\t\t\tconst fields = ctx.resolveTraceFields();\n\t\t\treturn fields ? [...args, fields] : args;\n\t\t} catch {\n\t\t\treturn args;\n\t\t}\n\t}\n\t/**\n\t* Export the record derived from the same native call to observability.\n\t* Mirrors DualLogger semantics: export is independent of the console\n\t* level filter, and never throws into the caller.\n\t*/\n\t#export(level, message, args) {\n\t\tconst ctx = this.#adapterContext;\n\t\tif (!ctx?.options.export) return;\n\t\ttry {\n\t\t\tctx.getLogSink()?.[level](message, buildLogRecordData(args));\n\t\t} catch {}\n\t}\n\tshouldLog(level, message, args) {\n\t\tif (!this.filter) return true;\n\t\ttry {\n\t\t\treturn this.filter({\n\t\t\t\tcomponent: this.component,\n\t\t\t\tlevel,\n\t\t\t\tmessage,\n\t\t\t\targs\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tconsole.error(`[Logger] Filter error for component=${this.component} level=${level}:`, e);\n\t\t\treturn true;\n\t\t}\n\t}\n\tprefix() {\n\t\treturn this.component ? `[${this.component}] ` : \"\";\n\t}\n\tdebug(message, ...args) {\n\t\tif (this.level === LogLevel.DEBUG && this.shouldLog(LogLevel.DEBUG, message, args)) console.info(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"debug\", message, args);\n\t}\n\tinfo(message, ...args) {\n\t\tif ((this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.INFO, message, args)) console.info(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"info\", message, args);\n\t}\n\twarn(message, ...args) {\n\t\tif ((this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.WARN, message, args)) console.warn(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"warn\", message, args);\n\t}\n\terror(message, ...args) {\n\t\tif ((this.level === LogLevel.ERROR || this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.ERROR, message, args)) console.error(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"error\", message, args);\n\t}\n\ttrackException(error, metadata) {\n\t\texportTrackedException(this.#adapterContext, error, metadata);\n\t}\n\tasync listLogs(_transportId, _params) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _params?.page ?? 1,\n\t\t\tperPage: _params?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n\tasync listLogsByRunId(_args) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _args.page ?? 1,\n\t\t\tperPage: _args.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n};\n//#endregion\nexport { ConsoleLogger, LogLevel, LoggerTransport, MastraLogger, RegisteredLogger, buildLogRecordData, createCustomTransport, exportTrackedException, isAdaptableLogger };\n\n//# sourceMappingURL=index.js.map","import { ConsoleLogger, RegisteredLogger } from \"../logger/index.js\";\n//#region src/base/MastraBase.ts\nvar MastraBase = class {\n\tcomponent = RegisteredLogger.LLM;\n\tlogger;\n\tname;\n\t#rawConfig;\n\tconstructor({ component, name, rawConfig }) {\n\t\tthis.component = component || RegisteredLogger.LLM;\n\t\tthis.name = name;\n\t\tthis.#rawConfig = rawConfig;\n\t\tthis.logger = new ConsoleLogger({ name: `${this.component} - ${this.name}` });\n\t}\n\t/**\n\t* Returns the raw storage configuration this primitive was created from,\n\t* or undefined if it was created from code.\n\t*/\n\ttoRawConfig() {\n\t\treturn this.#rawConfig;\n\t}\n\t/**\n\t* Sets the raw storage configuration for this primitive.\n\t* @internal\n\t*/\n\t__setRawConfig(rawConfig) {\n\t\tthis.#rawConfig = rawConfig;\n\t}\n\t/**\n\t* Set the logger for the agent\n\t* @param logger\n\t*/\n\t__setLogger(logger) {\n\t\tthis.logger = \"child\" in logger && typeof logger.child === \"function\" ? logger.child({ component: this.component }) : logger;\n\t}\n};\n//#endregion\nexport { MastraBase };\n\n//# sourceMappingURL=index.js.map","import { MastraBase } from \"@internal/core/base\";\nimport { PassThrough } from \"stream\";\nimport { experimental_generateSpeech, experimental_transcribe } from \"@internal/ai-sdk-v5\";\n//#region src/voice/voice.ts\nvar MastraVoice = class extends MastraBase {\n\tlisteningModel;\n\tspeechModel;\n\tspeaker;\n\trealtimeConfig;\n\tconstructor({ listeningModel, speechModel, speaker, realtimeConfig, name } = {}) {\n\t\tsuper({\n\t\t\tcomponent: \"VOICE\",\n\t\t\tname\n\t\t});\n\t\tthis.listeningModel = listeningModel;\n\t\tthis.speechModel = speechModel;\n\t\tthis.speaker = speaker;\n\t\tthis.realtimeConfig = realtimeConfig;\n\t}\n\t/**\n\t* Custom serialization for tracing/observability spans.\n\t* Excludes `apiKey` from listeningModel / speechModel / realtimeConfig\n\t* and any provider-specific state held by subclasses. Subclasses that\n\t* need to expose additional non-sensitive fields can override.\n\t*/\n\tserializeForSpan() {\n\t\treturn {\n\t\t\tcomponent: \"VOICE\",\n\t\t\tname: this.name,\n\t\t\tspeaker: this.speaker,\n\t\t\tlisteningModel: this.listeningModel ? { name: this.listeningModel.name } : void 0,\n\t\t\tspeechModel: this.speechModel ? { name: this.speechModel.name } : void 0,\n\t\t\trealtimeModel: this.realtimeConfig?.model\n\t\t};\n\t}\n\tupdateConfig(_options) {\n\t\tthis.logger.debug(\"updateConfig not implemented by this voice provider\");\n\t}\n\t/**\n\t* Initializes a WebSocket or WebRTC connection for real-time communication\n\t* @returns Promise that resolves when the connection is established\n\t*/\n\tasync connect(_options) {\n\t\tthis.logger.debug(\"connect not implemented by this voice provider\");\n\t}\n\t/**\n\t* Relay audio data to the voice provider for real-time processing\n\t* @param audioData Audio data to relay\n\t*/\n\tasync send(_audioData) {\n\t\tthis.logger.debug(\"relay not implemented by this voice provider\");\n\t}\n\t/**\n\t* Trigger voice providers to respond\n\t*/\n\tasync answer(_options) {\n\t\tthis.logger.debug(\"answer not implemented by this voice provider\");\n\t}\n\t/**\n\t* Equip the voice provider with instructions\n\t* @param instructions Instructions to add\n\t*/\n\taddInstructions(_instructions) {}\n\t/**\n\t* Equip the voice provider with tools\n\t* @param tools Array of tools to add\n\t*/\n\taddTools(_tools) {}\n\t/**\n\t* Disconnect from the WebSocket or WebRTC connection\n\t*/\n\tclose() {\n\t\tthis.logger.debug(\"close not implemented by this voice provider\");\n\t}\n\t/**\n\t* Register an event listener\n\t* @param event Event name (e.g., 'speaking', 'writing', 'error')\n\t* @param callback Callback function that receives event data\n\t*/\n\ton(_event, _callback) {\n\t\tthis.logger.debug(\"on not implemented by this voice provider\");\n\t}\n\t/**\n\t* Remove an event listener\n\t* @param event Event name (e.g., 'speaking', 'writing', 'error')\n\t* @param callback Callback function to remove\n\t*/\n\toff(_event, _callback) {\n\t\tthis.logger.debug(\"off not implemented by this voice provider\");\n\t}\n\t/**\n\t* Get available speakers/voices\n\t* @returns Array of available voice IDs and their metadata\n\t*/\n\tgetSpeakers() {\n\t\tthis.logger.debug(\"getSpeakers not implemented by this voice provider\");\n\t\treturn Promise.resolve([]);\n\t}\n\t/**\n\t* Get available speakers/voices\n\t* @returns Array of available voice IDs and their metadata\n\t*/\n\tgetListener() {\n\t\tthis.logger.debug(\"getListener not implemented by this voice provider\");\n\t\treturn Promise.resolve({ enabled: false });\n\t}\n};\n//#endregion\n//#region src/voice/aisdk/speech.ts\nvar AISDKSpeech = class extends MastraVoice {\n\tmodel;\n\tdefaultVoice;\n\tconstructor(model, options) {\n\t\tsuper({ name: \"ai-sdk-speech\" });\n\t\tthis.model = model;\n\t\tthis.defaultVoice = options?.voice;\n\t}\n\tasync speak(input, options) {\n\t\tconst text = typeof input === \"string\" ? input : await this.streamToText(input);\n\t\tconst result = await experimental_generateSpeech({\n\t\t\tmodel: this.model,\n\t\t\ttext,\n\t\t\tvoice: options?.speaker || this.defaultVoice,\n\t\t\tlanguage: options?.language,\n\t\t\tproviderOptions: options?.providerOptions,\n\t\t\tabortSignal: options?.abortSignal,\n\t\t\theaders: options?.headers\n\t\t});\n\t\tconst stream = new PassThrough();\n\t\tstream.end(Buffer.from(result.audio.uint8Array));\n\t\treturn stream;\n\t}\n\tasync listen() {\n\t\tthrow new Error(\"AI SDK speech models do not support transcription. Use AISDKTranscription instead.\");\n\t}\n\tasync getSpeakers() {\n\t\treturn [];\n\t}\n\tasync getListener() {\n\t\treturn { enabled: false };\n\t}\n\tasync streamToText(stream) {\n\t\tconst chunks = [];\n\t\tfor await (const chunk of stream) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\treturn Buffer.concat(chunks).toString(\"utf-8\");\n\t}\n};\n//#endregion\n//#region src/voice/aisdk/transcription.ts\nvar AISDKTranscription = class extends MastraVoice {\n\tmodel;\n\tconstructor(model) {\n\t\tsuper({ name: \"ai-sdk-transcription\" });\n\t\tthis.model = model;\n\t}\n\tasync speak() {\n\t\tthrow new Error(\"AI SDK transcription models do not support text-to-speech. Use AISDKSpeech instead.\");\n\t}\n\tasync getSpeakers() {\n\t\treturn [];\n\t}\n\tasync getListener() {\n\t\treturn { enabled: true };\n\t}\n\t/**\n\t* Transcribe audio to text\n\t* For enhanced metadata (segments, language, duration), use AI SDK's transcribe() directly\n\t*/\n\tasync listen(audioStream, options) {\n\t\tconst audioBuffer = await this.convertToBuffer(audioStream);\n\t\treturn (await experimental_transcribe({\n\t\t\tmodel: this.model,\n\t\t\taudio: audioBuffer,\n\t\t\tproviderOptions: options?.providerOptions,\n\t\t\tabortSignal: options?.abortSignal,\n\t\t\theaders: options?.headers\n\t\t})).text;\n\t}\n\tasync convertToBuffer(audio) {\n\t\tif (Buffer.isBuffer(audio)) return audio;\n\t\tif (audio instanceof Uint8Array) return Buffer.from(audio);\n\t\tif (typeof audio === \"string\") return Buffer.from(audio, \"base64\");\n\t\tconst chunks = [];\n\t\tfor await (const chunk of audio) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\treturn Buffer.concat(chunks);\n\t}\n};\n//#endregion\nexport { AISDKSpeech as n, MastraVoice as r, AISDKTranscription as t };\n\n//# sourceMappingURL=aisdk-C_K-VCwq.js.map","import { PassThrough } from 'node:stream';\nimport type { AudioConfig } from '../types';\n\n/**\n * Default audio configuration for Gemini Live API\n */\nexport const DEFAULT_AUDIO_CONFIG: AudioConfig = {\n inputSampleRate: 16000,\n outputSampleRate: 24000,\n encoding: 'pcm16',\n channels: 1,\n};\n\n/**\n * Manages audio streams for the Gemini Live Voice API\n * Handles speaker stream lifecycle, cleanup, and audio processing\n */\nexport class AudioStreamManager {\n private speakerStreams = new Map<string, PassThrough & { id?: string; created?: number }>();\n private currentResponseId?: string;\n private readonly MAX_CONCURRENT_STREAMS = 10;\n private readonly STREAM_TIMEOUT_MS = 30000; // 30 seconds\n private readonly debug: boolean;\n private readonly audioConfig: AudioConfig;\n private readonly maxChunkSize = 32768; // 32KB max chunk size per Gemini limits\n private readonly minSendInterval = 0; // No throttling - let the stream control the pace\n private lastSendTime = 0;\n private pendingChunks: Array<{ chunk: Buffer; timestamp: number }> = [];\n private pendingTimer?: NodeJS.Timeout;\n private sendToGemini?: (type: 'realtime_input' | 'client_content', message: Record<string, unknown>) => void;\n\n // Audio buffer management constants\n private readonly MAX_BUFFER_SIZE = 50 * 1024 * 1024; // 50MB maximum buffer size\n private readonly MAX_AUDIO_DURATION = 300; // 5 minutes maximum audio duration\n\n constructor(audioConfig: AudioConfig, debug: boolean = false) {\n this.audioConfig = audioConfig;\n this.debug = debug;\n }\n\n /**\n * Provide a sender callback that will be used to deliver messages to Gemini\n */\n setSender(sender: (type: 'realtime_input' | 'client_content', message: Record<string, unknown>) => void): void {\n this.sendToGemini = sender;\n }\n\n /**\n * Get the default audio configuration\n */\n static getDefaultAudioConfig(): AudioConfig {\n return { ...DEFAULT_AUDIO_CONFIG };\n }\n\n /**\n * Create a merged audio configuration with defaults\n */\n static createAudioConfig(customConfig?: Partial<AudioConfig>): AudioConfig {\n return {\n ...DEFAULT_AUDIO_CONFIG,\n ...customConfig,\n };\n }\n\n /**\n * Get the current response ID for the next audio chunk\n */\n getCurrentResponseId(): string | undefined {\n return this.currentResponseId;\n }\n\n /**\n * Set the current response ID for the next audio chunk\n */\n setCurrentResponseId(responseId: string): void {\n this.currentResponseId = responseId;\n }\n\n /**\n * Get the current speaker stream\n */\n getCurrentSpeakerStream(): NodeJS.ReadableStream | null {\n const currentResponseId = this.getCurrentResponseId();\n if (!currentResponseId) {\n return null;\n }\n\n const currentStream = this.speakerStreams.get(currentResponseId);\n return currentStream ? (currentStream as NodeJS.ReadableStream) : null;\n }\n\n /**\n * Add a new speaker stream for a response\n */\n addSpeakerStream(responseId: string, stream: PassThrough): void {\n const streamWithMetadata = Object.assign(stream, {\n id: responseId,\n created: Date.now(),\n });\n\n this.speakerStreams.set(responseId, streamWithMetadata);\n this.log(`Added speaker stream for response: ${responseId}`);\n\n // Enforce stream limits after adding\n this.enforceStreamLimits();\n }\n\n /**\n * Remove a specific speaker stream\n */\n removeSpeakerStream(responseId: string): void {\n const stream = this.speakerStreams.get(responseId);\n if (stream && !stream.destroyed) {\n stream.end();\n setTimeout(() => {\n if (!stream.destroyed) {\n stream.destroy();\n this.log(`Force destroyed stream for response: ${responseId}`);\n }\n }, 1000);\n }\n\n this.speakerStreams.delete(responseId);\n this.log(`Removed speaker stream for response: ${responseId}`);\n }\n\n /**\n * Clean up all speaker streams\n */\n cleanupSpeakerStreams(): void {\n try {\n if (this.speakerStreams.size === 0) {\n return;\n }\n\n this.log(`Cleaning up ${this.speakerStreams.size} speaker streams`);\n\n for (const [responseId, stream] of this.speakerStreams.entries()) {\n try {\n // Check if stream is already ended/destroyed\n if (!stream.destroyed) {\n stream.end();\n\n // Force destroy after a short timeout if end() doesn't work\n setTimeout(() => {\n if (!stream.destroyed) {\n stream.destroy();\n this.log(`Force destroyed stream for response: ${responseId}`);\n }\n }, 1000);\n }\n\n this.speakerStreams.delete(responseId);\n this.log(`Cleaned up speaker stream for response: ${responseId}`);\n } catch (streamError) {\n this.log(`Error cleaning up stream ${responseId}:`, streamError);\n // Force remove from map even if cleanup failed\n this.speakerStreams.delete(responseId);\n }\n }\n\n this.currentResponseId = undefined;\n this.log('All speaker streams cleaned up');\n } catch (error) {\n this.log('Error during speaker stream cleanup:', error);\n // Force clear the map if cleanup fails\n this.speakerStreams.clear();\n this.currentResponseId = undefined;\n }\n }\n\n /**\n * Clean up old/stale streams to prevent memory leaks\n */\n cleanupStaleStreams(): void {\n try {\n const now = Date.now();\n const staleCutoff = now - this.STREAM_TIMEOUT_MS;\n const staleStreams: string[] = [];\n\n for (const [responseId, stream] of this.speakerStreams.entries()) {\n const created = stream.created || 0;\n if (created < staleCutoff) {\n staleStreams.push(responseId);\n }\n }\n\n if (staleStreams.length > 0) {\n this.log(`Cleaning up ${staleStreams.length} stale streams`);\n for (const responseId of staleStreams) {\n const stream = this.speakerStreams.get(responseId);\n if (stream && !stream.destroyed) {\n stream.end();\n }\n this.speakerStreams.delete(responseId);\n }\n }\n } catch (error) {\n this.log('Error cleaning up stale streams:', error);\n }\n }\n\n /**\n * Enforce stream limits to prevent memory exhaustion\n */\n enforceStreamLimits(): void {\n try {\n if (this.speakerStreams.size <= this.MAX_CONCURRENT_STREAMS) {\n return;\n }\n\n this.log(\n `Stream limit exceeded (${this.speakerStreams.size}/${this.MAX_CONCURRENT_STREAMS}), cleaning up oldest streams`,\n );\n\n // Sort streams by creation time and remove oldest ones\n const sortedStreams = Array.from(this.speakerStreams.entries()).sort(\n ([, a], [, b]) => (a.created || 0) - (b.created || 0),\n );\n\n const streamsToRemove = sortedStreams.slice(0, this.speakerStreams.size - this.MAX_CONCURRENT_STREAMS);\n\n for (const [responseId, stream] of streamsToRemove) {\n if (!stream.destroyed) {\n stream.end();\n }\n this.speakerStreams.delete(responseId);\n this.log(`Removed old stream for response: ${responseId}`);\n }\n } catch (error) {\n this.log('Error enforcing stream limits:', error);\n }\n }\n\n /**\n * Get information about current streams for debugging\n */\n getStreamInfo(): {\n totalStreams: number;\n currentResponseId?: string;\n streamDetails: Array<{ responseId: string; created: number; destroyed: boolean }>;\n } {\n const streamDetails = Array.from(this.speakerStreams.entries()).map(([responseId, stream]) => ({\n responseId,\n created: stream.created || 0,\n destroyed: stream.destroyed,\n }));\n\n return {\n totalStreams: this.speakerStreams.size,\n currentResponseId: this.currentResponseId,\n streamDetails,\n };\n }\n\n /**\n * Convert Int16Array audio data to base64 string for WebSocket transmission\n */\n int16ArrayToBase64(int16Array: Int16Array): string {\n const buffer = new ArrayBuffer(int16Array.length * 2);\n const view = new DataView(buffer);\n\n // Convert Int16Array to bytes with little-endian format\n for (let i = 0; i < int16Array.length; i++) {\n view.setInt16(i * 2, int16Array[i]!, true);\n }\n\n const nodeBuffer = Buffer.from(buffer);\n return nodeBuffer.toString('base64');\n }\n\n /**\n * Convert base64 string to Int16Array audio data\n */\n base64ToInt16Array(base64Audio: string): Int16Array {\n try {\n const buffer = Buffer.from(base64Audio, 'base64');\n\n // Convert Buffer to Int16Array\n if (buffer.length % 2 !== 0) {\n throw new Error('Invalid audio data: buffer length must be even for 16-bit audio');\n }\n\n return new Int16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2);\n } catch (error) {\n throw new Error(\n `Failed to decode base64 audio data: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Validate and convert audio data to the required format for Gemini Live API\n * Gemini Live expects 16kHz PCM16 for input\n */\n validateAndConvertAudioInput(audioData: Buffer | Int16Array): Int16Array {\n if (Buffer.isBuffer(audioData)) {\n // Convert Buffer to Int16Array\n if (audioData.length % 2 !== 0) {\n throw new Error('Audio buffer length must be even for 16-bit audio');\n }\n return new Int16Array(audioData.buffer, audioData.byteOffset, audioData.byteLength / 2);\n }\n\n if (audioData instanceof Int16Array) {\n return audioData;\n }\n\n throw new Error('Unsupported audio data format. Expected Buffer or Int16Array');\n }\n\n /**\n * Process audio chunk for streaming - handles format validation and conversion\n */\n processAudioChunk(chunk: Buffer | Uint8Array | Int16Array): string {\n let int16Array: Int16Array;\n\n if (chunk instanceof Int16Array) {\n int16Array = chunk;\n } else if (Buffer.isBuffer(chunk)) {\n if (chunk.length % 2 !== 0) {\n throw new Error('Audio chunk length must be even for 16-bit audio');\n }\n int16Array = new Int16Array(chunk.buffer, chunk.byteOffset, chunk.byteLength / 2);\n } else if (chunk instanceof Uint8Array) {\n if (chunk.length % 2 !== 0) {\n throw new Error('Audio chunk length must be even for 16-bit audio');\n }\n int16Array = new Int16Array(chunk.buffer, chunk.byteOffset, chunk.byteLength / 2);\n } else {\n throw new Error('Unsupported audio chunk format');\n }\n\n return this.int16ArrayToBase64(int16Array);\n }\n\n /**\n * Validate audio format and sample rate for Gemini Live API requirements\n */\n validateAudioFormat(sampleRate?: number, channels?: number): void {\n if (sampleRate && sampleRate !== this.audioConfig.inputSampleRate) {\n this.log(\n `Warning: Audio sample rate ${sampleRate}Hz does not match expected ${this.audioConfig.inputSampleRate}Hz`,\n );\n }\n\n if (channels && channels !== this.audioConfig.channels) {\n throw new Error(`Unsupported channel count: ${channels}. Gemini Live API requires mono audio (1 channel)`);\n }\n }\n\n /**\n * Create an audio message for the Gemini Live API\n */\n createAudioMessage(audioData: string, messageType: 'input' | 'realtime' = 'realtime'): Record<string, unknown> {\n if (messageType === 'input') {\n // For conversation item creation (traditional listen method)\n return {\n client_content: {\n turns: [\n {\n role: 'user',\n parts: [\n {\n inlineData: {\n mimeType: 'audio/pcm',\n data: audioData,\n },\n },\n ],\n },\n ],\n turnComplete: true,\n },\n };\n } else {\n // For real-time streaming\n return {\n realtime_input: {\n audio: {\n mime_type: `audio/pcm;rate=${this.audioConfig.inputSampleRate ?? DEFAULT_AUDIO_CONFIG.inputSampleRate}`,\n data: audioData,\n },\n },\n };\n }\n }\n\n /**\n * Get a speaker stream by response ID\n */\n getSpeakerStream(responseId: string): PassThrough | undefined {\n return this.speakerStreams.get(responseId);\n }\n\n /**\n * Create a new speaker stream for a response ID\n */\n createSpeakerStream(responseId: string): PassThrough {\n const stream = new PassThrough() as PassThrough & { id?: string; created?: number };\n stream.id = responseId;\n stream.created = Date.now();\n\n // Add the stream to the manager\n this.addSpeakerStream(responseId, stream);\n\n return stream;\n }\n\n /**\n * Get the number of active streams\n */\n getActiveStreamCount(): number {\n return this.speakerStreams.size;\n }\n\n /**\n * Check if a specific response ID has an active stream\n */\n hasStream(responseId: string): boolean {\n return this.speakerStreams.has(responseId);\n }\n\n /**\n * Get all active response IDs\n */\n getActiveResponseIds(): string[] {\n return Array.from(this.speakerStreams.keys());\n }\n\n /**\n * Reset the manager state (useful for testing or reconnection)\n */\n reset(): void {\n this.cleanupSpeakerStreams();\n this.currentResponseId = undefined;\n this.log('AudioStreamManager reset');\n }\n\n /**\n * Validate audio chunk size and format\n */\n validateAudioChunk(chunk: Buffer): void {\n if (chunk.length === 0) {\n throw new Error('Audio chunk cannot be empty');\n }\n\n if (chunk.length > this.maxChunkSize) {\n throw new Error(`Audio chunk size ${chunk.length} exceeds maximum allowed size ${this.maxChunkSize}`);\n }\n\n if (chunk.length % 2 !== 0) {\n throw new Error('Audio chunk length must be even for 16-bit audio');\n }\n }\n\n /**\n * Send audio chunk with throttling and validation\n */\n sendAudioChunk(chunk: Buffer): void {\n try {\n this.validateAudioChunk(chunk);\n\n const now = Date.now();\n if (now - this.lastSendTime < this.minSendInterval) {\n // Throttle if needed - enqueue for later processing\n this.pendingChunks.push({ chunk, timestamp: now });\n const delay = this.minSendInterval - (now - this.lastSendTime);\n if (!this.pendingTimer) {\n this.pendingTimer = setTimeout(\n () => {\n this.pendingTimer = undefined;\n this.processPendingChunks();\n },\n Math.max(0, delay),\n );\n }\n return;\n }\n\n this.processChunk(chunk);\n this.processPendingChunks();\n } catch (error) {\n this.log('Error sending audio chunk:', error);\n throw error;\n }\n }\n\n /**\n * Handle audio stream processing\n */\n async handleAudioStream(stream: NodeJS.ReadableStream): Promise<void> {\n return new Promise((resolve, reject) => {\n const cleanup = () => {\n stream.removeAllListeners();\n };\n\n stream.on('data', (chunk: Buffer) => {\n try {\n if (chunk.length > this.maxChunkSize) {\n // Split large chunks\n const chunks = this.splitAudioChunk(chunk);\n for (const subChunk of chunks) {\n this.validateAudioChunk(subChunk);\n this.sendAudioChunk(subChunk);\n }\n } else {\n this.validateAudioChunk(chunk);\n this.sendAudioChunk(chunk);\n }\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n\n stream.on('end', () => {\n cleanup();\n resolve();\n });\n\n stream.on('error', error => {\n cleanup();\n reject(error);\n });\n });\n }\n\n /**\n * Split large audio chunks into smaller ones\n */\n private splitAudioChunk(chunk: Buffer): Buffer[] {\n const chunks: Buffer[] = [];\n let offset = 0;\n\n while (offset < chunk.length) {\n const size = Math.min(this.maxChunkSize, chunk.length - offset);\n chunks.push(chunk.subarray(offset, offset + size));\n offset += size;\n }\n\n return chunks;\n }\n\n /**\n * Calculate audio duration from buffer length\n */\n calculateAudioDuration(bufferLength: number, sampleRate?: number): number {\n const effectiveSampleRate = sampleRate || this.audioConfig.inputSampleRate;\n return bufferLength / (effectiveSampleRate * 2); // 2 bytes per sample for 16-bit audio\n }\n\n /**\n * Validate audio buffer size and duration\n */\n validateAudioBuffer(buffer: Buffer): void {\n if (buffer.length === 0) {\n throw new Error('Audio buffer cannot be empty');\n }\n\n if (buffer.length > this.MAX_BUFFER_SIZE) {\n throw new Error(\n `Audio buffer size ${buffer.length} exceeds maximum allowed size ${this.MAX_BUFFER_SIZE / (1024 * 1024)}MB`,\n );\n }\n\n if (buffer.length % 2 !== 0) {\n throw new Error('Audio buffer length must be even for 16-bit audio');\n }\n\n const duration = this.calculateAudioDuration(buffer.length);\n if (duration > this.MAX_AUDIO_DURATION) {\n throw new Error(\n `Audio duration ${duration.toFixed(2)}s exceeds maximum allowed duration ${this.MAX_AUDIO_DURATION}s`,\n );\n }\n }\n\n /**\n * Process audio buffer for transcription\n * Combines chunks, validates format, and converts to base64\n */\n processAudioBufferForTranscription(audioBuffer: Buffer): { base64Audio: string; duration: number; size: number } {\n // Validate audio format\n if (audioBuffer.length % 2 !== 0) {\n throw new Error('Invalid audio data: buffer length must be even for 16-bit audio');\n }\n\n // Calculate duration\n const duration = this.calculateAudioDuration(audioBuffer.length);\n\n // Convert to base64\n const base64Audio = audioBuffer.toString('base64');\n\n return {\n base64Audio,\n duration,\n size: audioBuffer.length,\n };\n }\n\n /**\n * Process audio chunks for transcription with buffer management\n * Handles chunk collection, size validation, and buffer management\n */\n processAudioChunksForTranscription(\n chunks: Buffer[],\n totalBufferSize: number,\n ): { audioBuffer: Buffer; base64Audio: string; duration: number; size: number } {\n // Check buffer size to prevent memory overflow\n if (totalBufferSize > this.MAX_BUFFER_SIZE) {\n throw new Error(`Audio data exceeds maximum size of ${this.MAX_BUFFER_SIZE / (1024 * 1024)}MB`);\n }\n\n // Combine all chunks\n const audioBuffer = Buffer.concat(chunks);\n\n // Process for transcription\n const result = this.processAudioBufferForTranscription(audioBuffer);\n\n return {\n audioBuffer,\n ...result,\n };\n }\n\n /**\n * Validate audio chunks and calculate total size\n */\n validateAudioChunks(chunks: Buffer[]): { totalSize: number; isValid: boolean; error?: string } {\n let totalSize = 0;\n\n for (const chunk of chunks) {\n if (!Buffer.isBuffer(chunk)) {\n return { totalSize: 0, isValid: false, error: 'Invalid chunk format' };\n }\n\n totalSize += chunk.length;\n\n if (totalSize > this.MAX_BUFFER_SIZE) {\n return {\n totalSize,\n isValid: false,\n error: `Total size ${totalSize} exceeds maximum allowed size ${this.MAX_BUFFER_SIZE}`,\n };\n }\n }\n\n return { totalSize, isValid: true };\n }\n\n /**\n * Get audio buffer limits and configuration\n */\n getAudioBufferLimits(): { maxBufferSize: number; maxAudioDuration: number; maxChunkSize: number } {\n return {\n maxBufferSize: this.MAX_BUFFER_SIZE,\n maxAudioDuration: this.MAX_AUDIO_DURATION,\n maxChunkSize: this.maxChunkSize,\n };\n }\n\n /**\n * Get audio configuration\n */\n getAudioConfig(): AudioConfig {\n return this.audioConfig;\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[AudioStreamManager] ${message}`, ...args);\n }\n }\n\n /**\n * Handle complete audio transcription workflow\n * Manages stream processing, chunk collection, and transcription\n */\n async handleAudioTranscription(\n audioStream: NodeJS.ReadableStream,\n sendAndAwaitTranscript: (base64Audio: string) => Promise<string>,\n onError: (error: Error) => void,\n timeoutMs: number = 30000,\n ): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let isCleanedUp = false;\n let totalBufferSize = 0;\n let isResolved = false;\n\n // Set up timeout\n const timeout = setTimeout(() => {\n if (!isResolved) {\n cleanup();\n reject(new Error(`Transcription timeout - no response received within ${timeoutMs / 1000} seconds`));\n }\n }, timeoutMs);\n\n // Stream event handlers\n const onStreamData = (chunk: Buffer) => {\n try {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\n // Check buffer size to prevent memory overflow\n totalBufferSize += buffer.length;\n if (totalBufferSize > this.MAX_BUFFER_SIZE) {\n cleanup();\n reject(new Error(`Audio data exceeds maximum size of ${this.MAX_BUFFER_SIZE / (1024 * 1024)}MB`));\n return;\n }\n\n chunks.push(buffer);\n } catch (error) {\n cleanup();\n reject(\n new Error(`Failed to process audio chunk: ${error instanceof Error ? error.message : 'Unknown error'}`),\n );\n }\n };\n\n const onStreamError = (error: Error) => {\n cleanup();\n reject(new Error(`Audio stream error: ${error.message}`));\n };\n\n const onStreamEnd = async () => {\n try {\n // Remove stream listeners as we're done with the stream\n audioStream.removeListener('data', onStreamData);\n audioStream.removeListener('error', onStreamError);\n\n // Process chunks for transcription\n const result = this.processAudioChunksForTranscription(chunks, totalBufferSize);\n\n this.log('Processing audio for transcription:', {\n chunks: chunks.length,\n totalSize: result.size,\n duration: result.duration,\n });\n\n // Send audio and await the transcript from the caller\n try {\n const transcript = await sendAndAwaitTranscript(result.base64Audio);\n if (!isResolved) {\n isResolved = true;\n cleanup();\n resolve(transcript.trim());\n }\n } catch (error) {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(\n new Error(\n `Failed to obtain transcription: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ),\n );\n }\n }\n } catch (error) {\n cleanup();\n reject(\n new Error(`Failed to process audio stream: ${error instanceof Error ? error.message : 'Unknown error'}`),\n );\n }\n };\n\n // Comprehensive cleanup function\n const cleanup = () => {\n if (isCleanedUp) return; // Prevent double cleanup\n isCleanedUp = true;\n\n // Clear all timers\n clearTimeout(timeout);\n\n // Remove stream event listeners\n audioStream.removeListener('data', onStreamData);\n audioStream.removeListener('error', onStreamError);\n audioStream.removeListener('end', onStreamEnd);\n\n // Clear chunks array to free memory\n chunks.length = 0;\n };\n\n // Set up stream event listeners\n audioStream.on('data', onStreamData);\n audioStream.on('error', onStreamError);\n audioStream.on('end', onStreamEnd);\n });\n }\n\n private processChunk(chunk: Buffer): void {\n // Convert raw audio buffer to base64 PCM16 and build message\n const base64Audio = this.processAudioChunk(chunk);\n const message = this.createAudioMessage(base64Audio, 'realtime');\n\n // Send via injected sender\n if (this.sendToGemini) {\n this.sendToGemini('realtime_input', message);\n } else {\n this.log('No sender configured for AudioStreamManager; dropping audio chunk');\n }\n\n // Update throttle timing and log\n this.lastSendTime = Date.now();\n this.log(`Sent audio chunk of size: ${chunk.length} bytes`);\n }\n\n private processPendingChunks(): void {\n while (this.pendingChunks.length > 0) {\n const nextChunk = this.pendingChunks[0];\n const now = Date.now();\n if (nextChunk && now - this.lastSendTime >= this.minSendInterval) {\n this.pendingChunks.shift();\n this.processChunk(nextChunk.chunk);\n } else {\n const delay = this.minSendInterval - (now - this.lastSendTime);\n if (!this.pendingTimer) {\n this.pendingTimer = setTimeout(\n () => {\n this.pendingTimer = undefined;\n this.processPendingChunks();\n },\n Math.max(0, delay),\n );\n }\n break;\n }\n }\n }\n}\n","import type { GeminiLiveErrorCode } from '../types';\n\n/**\n * Helper class for consistent error handling across managers and provider\n */\nexport class GeminiLiveError extends Error {\n public readonly code: string;\n public readonly details?: unknown;\n public readonly timestamp: number;\n\n constructor(code: GeminiLiveErrorCode | string, message: string, details?: unknown) {\n super(message);\n this.name = 'GeminiLiveError';\n this.code = code;\n this.details = details;\n this.timestamp = Date.now();\n }\n\n toEventData() {\n return {\n message: this.message,\n code: this.code,\n details: this.details,\n timestamp: this.timestamp,\n };\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { WebSocket } from 'ws';\nimport { GeminiLiveErrorCode } from '../types';\nimport { GeminiLiveError } from '../utils/errors';\n\nexport interface ConnectionConfig {\n debug: boolean;\n timeoutMs?: number;\n}\n\nexport class ConnectionManager {\n private ws?: WebSocket;\n private eventEmitter: EventEmitter;\n private readonly debug: boolean;\n private readonly timeoutMs: number;\n\n constructor(config: ConnectionConfig) {\n this.eventEmitter = new EventEmitter();\n this.debug = config.debug;\n this.timeoutMs = config.timeoutMs || 30000;\n }\n\n /**\n * Set the WebSocket instance\n */\n setWebSocket(ws: WebSocket): void {\n this.ws = ws;\n }\n\n /**\n * Get the current WebSocket instance\n */\n getWebSocket(): WebSocket | undefined {\n return this.ws;\n }\n\n /**\n * Check if WebSocket is connected\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Check if WebSocket is connecting\n */\n isConnecting(): boolean {\n return this.ws?.readyState === WebSocket.CONNECTING;\n }\n\n /**\n * Check if WebSocket is closed\n */\n isClosed(): boolean {\n return this.ws?.readyState === WebSocket.CLOSED;\n }\n\n /**\n * Wait for WebSocket to open\n */\n async waitForOpen(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.ws) {\n reject(new Error('WebSocket not initialized'));\n return;\n }\n\n // If already open, resolve immediately\n if (this.ws.readyState === WebSocket.OPEN) {\n resolve();\n return;\n }\n\n // Set up event listeners with cleanup\n const onOpen = () => {\n cleanup();\n resolve();\n };\n\n const onError = (error: Error) => {\n cleanup();\n reject(new Error(`WebSocket connection failed: ${error.message}`));\n };\n\n const onClose = () => {\n cleanup();\n reject(new Error('WebSocket connection closed before opening'));\n };\n\n const cleanup = () => {\n this.ws?.removeListener('open', onOpen);\n this.ws?.removeListener('error', onError);\n this.ws?.removeListener('close', onClose);\n };\n\n // Add event listeners\n this.ws.once('open', onOpen);\n this.ws.once('error', onError);\n this.ws.once('close', onClose);\n\n // Add timeout to prevent hanging indefinitely\n setTimeout(() => {\n cleanup();\n reject(new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_FAILED, 'WebSocket connection timeout'));\n }, this.timeoutMs);\n });\n }\n\n /**\n * Send data through WebSocket\n */\n send(data: string | Buffer): void {\n if (!this.ws) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket not initialized');\n }\n\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket is not open');\n }\n\n this.ws.send(data);\n }\n\n /**\n * Close the WebSocket connection\n */\n close(): void {\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n }\n\n /**\n * Get connection state\n */\n getConnectionState(): 'disconnected' | 'connecting' | 'connected' | 'closed' {\n if (!this.ws) return 'disconnected';\n\n switch (this.ws.readyState) {\n case WebSocket.CONNECTING:\n return 'connecting';\n case WebSocket.OPEN:\n return 'connected';\n case WebSocket.CLOSED:\n return 'closed';\n default:\n return 'disconnected';\n }\n }\n\n /**\n * Validate WebSocket state for operations\n */\n validateWebSocketState(): void {\n if (!this.ws) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket not initialized');\n }\n\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket is not open');\n }\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[ConnectionManager] ${message}`, ...args);\n }\n }\n}\n","export interface ContextEntry {\n role: 'user' | 'assistant';\n content: string;\n timestamp: number;\n}\n\nexport interface ContextConfig {\n maxEntries?: number;\n maxContentLength?: number;\n compressionThreshold?: number;\n compressionEnabled?: boolean;\n}\n\nexport class ContextManager {\n private contextHistory: ContextEntry[] = [];\n private readonly maxEntries: number;\n private readonly maxContentLength: number;\n private readonly compressionThreshold: number;\n private compressionEnabled: boolean;\n\n constructor(config: ContextConfig = {}) {\n this.maxEntries = config.maxEntries || 100;\n this.maxContentLength = config.maxContentLength || 10000; // 10KB per entry\n this.compressionThreshold = config.compressionThreshold || 50;\n this.compressionEnabled = config.compressionEnabled ?? false;\n }\n\n /**\n * Add entry to context history\n */\n addEntry(role: 'user' | 'assistant', content: string): void {\n // Validate content length\n let processedContent = content;\n if (content.length > this.maxContentLength) {\n processedContent = content.substring(0, this.maxContentLength) + '...';\n }\n\n const entry: ContextEntry = {\n role,\n content: processedContent,\n timestamp: Date.now(),\n };\n\n this.contextHistory.push(entry);\n\n if (this.contextHistory.length > this.maxEntries) {\n if (this.compressionEnabled) {\n this.compressContext();\n } else {\n this.contextHistory = this.contextHistory.slice(-this.maxEntries);\n }\n }\n }\n\n /**\n * Get context history\n */\n getContextHistory(): ContextEntry[] {\n return [...this.contextHistory];\n }\n\n /**\n * Get context history as array of role/content pairs\n */\n getContextArray(): Array<{ role: string; content: string }> {\n return this.contextHistory.map(entry => ({\n role: entry.role,\n content: entry.content,\n }));\n }\n\n /**\n * Clear context history\n */\n clearContext(): void {\n this.contextHistory = [];\n }\n\n /**\n * Get context size\n */\n getContextSize(): number {\n return this.contextHistory.length;\n }\n\n /**\n * Compress context when it exceeds threshold\n */\n private compressContext(): void {\n if (!this.compressionEnabled || this.contextHistory.length <= this.compressionThreshold) {\n return;\n }\n\n // Keep first and last entries, compress middle ones\n const keepCount = Math.floor(this.compressionThreshold / 3);\n const firstEntries = this.contextHistory.slice(0, keepCount);\n const lastEntries = this.contextHistory.slice(-keepCount);\n\n // Create compressed middle entry\n const middleEntries = this.contextHistory.slice(keepCount, -keepCount);\n if (middleEntries.length > 0) {\n const compressedEntry: ContextEntry = {\n role: 'assistant',\n content: `[Compressed ${middleEntries.length} previous messages]`,\n timestamp: Date.now(),\n };\n\n this.contextHistory = [...firstEntries, compressedEntry, ...lastEntries];\n } else {\n this.contextHistory = [...firstEntries, ...lastEntries];\n }\n }\n\n /**\n * Enable or disable compression at runtime\n */\n setCompressionEnabled(enabled: boolean): void {\n this.compressionEnabled = enabled;\n }\n\n /**\n * Get context summary\n */\n getContextSummary(): {\n totalEntries: number;\n userEntries: number;\n assistantEntries: number;\n oldestTimestamp: number | null;\n newestTimestamp: number | null;\n } {\n if (this.contextHistory.length === 0) {\n return {\n totalEntries: 0,\n userEntries: 0,\n assistantEntries: 0,\n oldestTimestamp: null,\n newestTimestamp: null,\n };\n }\n\n const userEntries = this.contextHistory.filter(entry => entry.role === 'user').length;\n const assistantEntries = this.contextHistory.filter(entry => entry.role === 'assistant').length;\n const timestamps = this.contextHistory.map(entry => entry.timestamp);\n\n return {\n totalEntries: this.contextHistory.length,\n userEntries,\n assistantEntries,\n oldestTimestamp: Math.min(...timestamps),\n newestTimestamp: Math.max(...timestamps),\n };\n }\n\n /**\n * Search context for specific content\n */\n searchContext(query: string, role?: 'user' | 'assistant'): ContextEntry[] {\n const searchQuery = query.toLowerCase();\n\n return this.contextHistory.filter(entry => {\n const matchesRole = role ? entry.role === role : true;\n const matchesContent = entry.content.toLowerCase().includes(searchQuery);\n\n return matchesRole && matchesContent;\n });\n }\n\n /**\n * Get recent context entries\n */\n getRecentEntries(count: number): ContextEntry[] {\n return this.contextHistory.slice(-count);\n }\n\n /**\n * Get context entries by role\n */\n getEntriesByRole(role: 'user' | 'assistant'): ContextEntry[] {\n return this.contextHistory.filter(entry => entry.role === role);\n }\n}\n","import { GoogleAuth } from 'google-auth-library';\nimport { GeminiLiveErrorCode } from '../types';\nimport type { AuthOptions } from '../types';\nimport { GeminiLiveError } from '../utils/errors';\n\nexport interface AuthConfig {\n apiKey?: string;\n vertexAI?: boolean;\n project?: string;\n debug: boolean;\n serviceAccountKeyFile?: string;\n serviceAccountEmail?: string;\n tokenExpirationTime?: number;\n}\n\nexport class AuthManager {\n private authClient?: GoogleAuth;\n private accessToken?: string;\n private tokenExpirationTime?: number;\n private readonly config: AuthConfig;\n\n constructor(config: AuthConfig) {\n this.config = config;\n this.tokenExpirationTime = config.tokenExpirationTime ?? 50 * 60 * 1000;\n }\n\n /**\n * Initialize authentication based on configuration\n */\n async initialize(): Promise<void> {\n if (this.config.vertexAI) {\n await this.initializeVertexAI();\n } else if (this.config.apiKey) {\n // API key auth doesn't need initialization\n return;\n } else {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.API_KEY_MISSING,\n 'Either API key or Vertex AI configuration is required',\n );\n }\n }\n\n /**\n * Initialize Vertex AI authentication\n */\n private async initializeVertexAI(): Promise<void> {\n if (!this.config.project) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.PROJECT_ID_MISSING,\n 'Google Cloud project ID is required when using Vertex AI',\n );\n }\n\n const authOptions: AuthOptions = {\n scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n projectId: this.config.project,\n };\n\n // Support service account key file\n if (this.config.serviceAccountKeyFile) {\n authOptions.keyFilename = this.config.serviceAccountKeyFile;\n this.log('Using service account key file for authentication:', this.config.serviceAccountKeyFile);\n }\n\n // Support impersonation via service account email\n if (this.config.serviceAccountEmail) {\n authOptions.clientOptions = { subject: this.config.serviceAccountEmail };\n this.log('Using service account impersonation:', this.config.serviceAccountEmail);\n }\n\n try {\n this.authClient = new GoogleAuth(authOptions);\n } catch (error) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.AUTHENTICATION_FAILED,\n `Failed to initialize Vertex AI authentication: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Get access token for Vertex AI\n */\n async getAccessToken(): Promise<string> {\n if (!this.config.vertexAI) {\n throw new GeminiLiveError(GeminiLiveErrorCode.AUTHENTICATION_FAILED, 'Vertex AI authentication not configured');\n }\n\n if (!this.authClient) {\n throw new GeminiLiveError(GeminiLiveErrorCode.AUTHENTICATION_FAILED, 'Authentication client not initialized');\n }\n\n // Check if we have a valid cached token\n if (this.accessToken && this.tokenExpirationTime && Date.now() < this.tokenExpirationTime) {\n return this.accessToken;\n }\n\n try {\n const client = await this.authClient.getClient();\n const token = await client.getAccessToken();\n\n if (!token.token) {\n throw new Error('No access token received');\n }\n\n this.accessToken = token.token;\n\n // Set expiry time (tokens typically last 1 hour, so set to 50 minutes to be safe)\n this.tokenExpirationTime = Date.now() + 50 * 60 * 1000;\n\n this.log('Successfully obtained new access token');\n return this.accessToken;\n } catch (error) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.AUTHENTICATION_FAILED,\n `Failed to get access token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Get API key if using API key authentication\n */\n getApiKey(): string | undefined {\n if (this.config.vertexAI) {\n return undefined;\n }\n return this.config.apiKey;\n }\n\n /**\n * Check if using Vertex AI authentication\n */\n isUsingVertexAI(): boolean {\n return this.config.vertexAI === true;\n }\n\n /**\n * Check if authentication is configured\n */\n isConfigured(): boolean {\n return !!(this.config.apiKey || (this.config.vertexAI && this.config.project));\n }\n\n /**\n * Check if access token is valid\n */\n hasValidToken(): boolean {\n if (!this.config.vertexAI) return false;\n return !!(this.accessToken && this.tokenExpirationTime && Date.now() < this.tokenExpirationTime);\n }\n\n /**\n * Clear cached authentication data\n */\n clearCache(): void {\n this.accessToken = undefined;\n this.tokenExpirationTime = undefined;\n }\n\n /**\n * Get authentication configuration\n */\n getConfig(): AuthConfig {\n return { ...this.config };\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.config.debug) {\n console.info(`[AuthManager] ${message}`, ...args);\n }\n }\n}\n","import { EventEmitter } from 'node:events';\n// Make the event manager generic over an event map\nexport type EventMap = Record<string, unknown>;\n\nexport interface EventConfig {\n debug: boolean;\n}\n\nexport class EventManager<TEvents extends EventMap = Record<string, unknown>> {\n private eventEmitter: EventEmitter;\n private readonly debug: boolean;\n private eventCounts: Record<string, number> = {};\n\n constructor(config: EventConfig) {\n this.eventEmitter = new EventEmitter();\n this.debug = config.debug;\n }\n\n /**\n * Emit an event with data\n */\n emit<K extends Extract<keyof TEvents, string>>(event: K, data: TEvents[K]): boolean {\n this.incrementEventCount(event);\n const result = this.eventEmitter.emit(event, data);\n\n if (this.debug) {\n this.log(`Emitted event: ${event}`, data);\n }\n\n return result;\n }\n\n /**\n * Add event listener\n */\n on<E extends Extract<keyof TEvents, string>>(event: E, callback: (data: TEvents[E]) => void): void {\n this.eventEmitter.on(event, callback);\n\n if (this.debug) {\n this.log(`Added listener for event: ${event}`);\n }\n }\n\n /**\n * Remove event listener\n */\n off<E extends Extract<keyof TEvents, string>>(event: E, callback: (data: TEvents[E]) => void): void {\n this.eventEmitter.off(event, callback);\n\n if (this.debug) {\n this.log(`Removed listener for event: ${event}`);\n }\n }\n\n /**\n * Add one-time event listener\n */\n once<E extends Extract<keyof TEvents, string>>(event: E, callback: (data: TEvents[E]) => void): void {\n this.eventEmitter.once(event, callback);\n\n if (this.debug) {\n this.log(`Added one-time listener for event: ${event}`);\n }\n }\n\n /**\n * Remove all listeners for an event\n */\n removeAllListeners(event?: string): void {\n this.eventEmitter.removeAllListeners(event);\n\n if (this.debug) {\n this.log(`Removed all listeners${event ? ` for event: ${event}` : ''}`);\n }\n }\n\n /**\n * Get event listener count\n */\n getListenerCount(event: string): number {\n return this.eventEmitter.listenerCount(event);\n }\n\n /**\n * Get event listener info\n */\n getEventListenerInfo(): Record<string, number> {\n const events = this.eventEmitter.eventNames();\n const info: Record<string, number> = {};\n\n events.forEach(event => {\n const eventName = typeof event === 'string' ? event : event.toString();\n info[eventName] = this.eventEmitter.listenerCount(event);\n });\n\n return info;\n }\n\n /**\n * Get event emission counts\n */\n getEventCounts(): Record<string, number> {\n return { ...this.eventCounts };\n }\n\n /**\n * Reset event counts\n */\n resetEventCounts(): void {\n this.eventCounts = {};\n }\n\n /**\n * Clean up event listeners\n */\n cleanup(): void {\n this.eventEmitter.removeAllListeners();\n this.resetEventCounts();\n\n if (this.debug) {\n this.log('Cleaned up all event listeners');\n }\n }\n\n /**\n * Get the underlying EventEmitter\n */\n getEventEmitter(): EventEmitter {\n return this.eventEmitter;\n }\n\n /**\n * Increment event count for tracking\n */\n private incrementEventCount(event: string): void {\n this.eventCounts[event] = (this.eventCounts[event] || 0) + 1;\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[EventManager] ${message}`, ...args);\n }\n }\n}\n","import { MastraVoice } from '@internal/voice';\nimport type { ToolsInput, VoiceEventType, VoiceConfig } from '@internal/voice';\nimport { GoogleSchemaCompatLayer } from '@mastra/schema-compat';\nimport type { WebSocket as WSType } from 'ws';\nimport { WebSocket } from 'ws';\nimport { AudioStreamManager, ConnectionManager, ContextManager, AuthManager, EventManager } from './managers';\nimport { GeminiLiveErrorCode } from './types';\nimport type {\n GeminiLiveVoiceConfig,\n GeminiLiveVoiceOptions,\n GeminiLiveEventMap,\n GeminiVoiceModel,\n GeminiVoiceName,\n GeminiToolConfig,\n AudioConfig,\n GeminiLiveServerMessage,\n GeminiSessionConfig,\n IncrementalTurn,\n UpdateMessage,\n} from './types';\nimport { GeminiLiveError } from './utils/errors';\n\n// Narrow event keys to strings for the typed EventManager\ntype GeminiEventName = Extract<keyof GeminiLiveEventMap, string>;\n\n/**\n * Default configuration values\n */\nconst DEFAULT_MODEL: GeminiVoiceModel = 'gemini-3.1-flash-live-preview';\nconst DEFAULT_VOICE: GeminiVoiceName = 'Puck';\n\n// How many recently handled tool call ids to keep for duplicate suppression.\n// Insertion-ordered; the oldest id is evicted once the bound is reached.\nconst MAX_TRACKED_TOOL_CALL_IDS = 512;\n\n// Treats only plain objects (own prototype chain ends at `Object.prototype` or `null`) as\n// proto-struct compatible — `Date`, `Map`, `Set`, `Error`, `RegExp`, and class instances all\n// JSON-serialize to `{}` if forwarded bare and need wrapping for Gemini Live's `response` field.\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/**\n * Helper class for consistent error handling\n */\n// GeminiLiveError is now defined in types.ts for reuse across managers\n\n/**\n * GeminiLiveVoice provides real-time multimodal voice interactions using Google's Gemini Live API.\n *\n * Features:\n * - Bidirectional audio streaming\n * - Built-in VAD and interrupt handling\n * - Tool calling capabilities\n * - Session management and resumption\n * - Live transcription\n * - Support for both Gemini API and Vertex AI\n *\n * @example Backward compatibility - Direct options (legacy)\n * ```typescript\n * const voice = new GeminiLiveVoice({\n * apiKey: 'your-api-key',\n * model: 'gemini-2.0-flash-live-001',\n * speaker: 'Puck',\n * instructions: 'You are a helpful assistant'\n * });\n * ```\n *\n * @example Mastra VoiceConfig pattern - Recommended\n * ```typescript\n * const voice = new GeminiLiveVoice({\n * speechModel: { name: 'gemini-2.0-flash-live-001', apiKey: 'your-api-key' },\n * speaker: 'Puck',\n * realtimeConfig: {\n * model: 'gemini-2.0-flash-live-001',\n * apiKey: 'your-api-key',\n * options: {\n * instructions: 'You are a helpful assistant',\n * debug: true\n * }\n * }\n * });\n * ```\n *\n * @example Using Vertex AI (with OAuth)\n * ```typescript\n * const voice = new GeminiLiveVoice({\n * realtimeConfig: {\n * model: 'gemini-2.0-flash-live-001',\n * options: {\n * vertexAI: true,\n * project: 'your-gcp-project',\n * location: 'us-central1',\n * serviceAccountKeyFile: '/path/to/service-account.json',\n * }\n * }\n * });\n * ```\n */\nexport class GeminiLiveVoice extends MastraVoice<\n GeminiLiveVoiceConfig,\n GeminiLiveVoiceOptions,\n GeminiLiveVoiceOptions,\n ToolsInput,\n GeminiLiveEventMap\n> {\n private ws?: WSType;\n private eventManager: EventManager;\n private state: 'disconnected' | 'connected' = 'disconnected';\n private sessionHandle?: string;\n private readonly debug: boolean;\n private readonly audioConfig: AudioConfig;\n private queue: unknown[] = [];\n\n // Managers\n private connectionManager: ConnectionManager;\n private contextManager: ContextManager;\n private authManager: AuthManager;\n\n // Audio chunk concatenation - optimized stream management\n private audioStreamManager: AudioStreamManager;\n\n // Session management properties\n private sessionId?: string;\n private sessionStartTime?: number;\n private isResuming = false;\n private sessionDurationTimeout?: NodeJS.Timeout;\n\n // Tool integration properties\n private tools?: ToolsInput;\n private requestContext?: any;\n // Provider call ids already handled on this connection. Gemini Live can deliver the same\n // function call through both inbound representations — a top-level `toolCall` message and\n // `serverContent.modelTurn.parts[].functionCall` — so deduplicate by call id to execute\n // each call exactly once. Calls without a provider id get a fresh UUID and are never\n // suppressed.\n private processedToolCallIds = new Set<string>();\n\n // Store the configuration options\n private options: GeminiLiveVoiceConfig;\n\n // Accumulates assistant text across `serverContent` frames for the current\n // turn. Live API streams responses over many frames; we aggregate here and\n // flush to context history once on `turnComplete`.\n private pendingAssistantResponse = '';\n\n /**\n * Normalize configuration to ensure proper VoiceConfig format\n * Handles backward compatibility with direct GeminiLiveVoiceConfig\n * @private\n */\n private static normalizeConfig(\n config: VoiceConfig<GeminiLiveVoiceConfig> | GeminiLiveVoiceConfig,\n ): VoiceConfig<GeminiLiveVoiceConfig> {\n // Check if this is already a proper VoiceConfig (has realtimeConfig or standard VoiceConfig properties)\n if ('realtimeConfig' in config || 'speechModel' in config || 'listeningModel' in config) {\n return config as VoiceConfig<GeminiLiveVoiceConfig>;\n }\n\n // Convert direct GeminiLiveVoiceConfig to VoiceConfig format\n const geminiConfig = config as GeminiLiveVoiceConfig;\n return {\n speechModel: {\n name: geminiConfig.model || DEFAULT_MODEL,\n apiKey: geminiConfig.apiKey,\n },\n speaker: geminiConfig.speaker || DEFAULT_VOICE,\n realtimeConfig: {\n model: geminiConfig.model || DEFAULT_MODEL,\n apiKey: geminiConfig.apiKey,\n options: geminiConfig,\n },\n };\n }\n\n /**\n * Creates a new GeminiLiveVoice instance\n *\n * @param config Configuration options\n */\n constructor(config: VoiceConfig<GeminiLiveVoiceConfig> | GeminiLiveVoiceConfig = {}) {\n // Handle backward compatibility - if config has Gemini-specific properties directly,\n // convert to proper VoiceConfig format\n const normalizedConfig = GeminiLiveVoice.normalizeConfig(config);\n super(normalizedConfig);\n\n // Seed `this.options` from `realtimeConfig`. Fields on the `realtimeConfig` root\n // (`model`, `apiKey`, `vertexAI`, `project`, etc.) and on the inner `options` object both\n // belong in the flat `GeminiLiveVoiceConfig` shape this class reads from; merge with the\n // explicit inner `options` last so caller intent on the inner object wins on collisions.\n const realtimeConfig = normalizedConfig.realtimeConfig;\n if (realtimeConfig) {\n const { options: innerOptions, ...realtimeConfigRoot } = realtimeConfig;\n this.options = { ...(realtimeConfigRoot as Partial<GeminiLiveVoiceConfig>), ...(innerOptions || {}) };\n } else {\n this.options = {};\n }\n\n // `speaker` lives at the `VoiceConfig` root, sibling to `realtimeConfig` — not inside it.\n // Propagate explicitly so `new GeminiLiveVoice({ speaker: 'Puck', realtimeConfig: { ... } })`\n // honors the caller's voice. Reading from `normalizedConfig.speaker` would pin `DEFAULT_VOICE`\n // whenever the flat-config branch normalized without an explicit speaker, so use the raw `config`.\n if ('realtimeConfig' in config && config.speaker && !this.options.speaker) {\n this.options.speaker = config.speaker as GeminiVoiceName;\n }\n\n // Validate API key\n const apiKey = this.options.apiKey;\n if (!apiKey && !this.options.vertexAI) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.API_KEY_MISSING,\n 'Google API key is required. Set GOOGLE_API_KEY environment variable or pass apiKey to constructor',\n );\n }\n\n this.debug = this.options.debug || false;\n\n // Merge provided audio config with defaults\n this.audioConfig = {\n ...AudioStreamManager.getDefaultAudioConfig(),\n ...this.options.audioConfig,\n };\n\n // Initialize AudioStreamManager\n this.audioStreamManager = new AudioStreamManager(this.audioConfig, this.debug);\n // Inject sender so AudioStreamManager can deliver realtime audio\n this.audioStreamManager.setSender((type, message) => this.sendEvent(type, message));\n\n this.eventManager = new EventManager<GeminiLiveEventMap>({ debug: this.debug });\n this.connectionManager = new ConnectionManager({ debug: this.debug, timeoutMs: 30000 });\n this.contextManager = new ContextManager({\n maxEntries: 100,\n compressionThreshold: 50,\n compressionEnabled: this.options.sessionConfig?.contextCompression ?? false,\n });\n this.authManager = new AuthManager({\n apiKey: this.options.apiKey,\n vertexAI: this.options.vertexAI,\n project: this.options.project,\n serviceAccountKeyFile: this.options.serviceAccountKeyFile,\n serviceAccountEmail: this.options.serviceAccountEmail,\n debug: this.debug,\n tokenExpirationTime: this.options.tokenExpirationTime,\n });\n\n if (this.options.vertexAI && !this.options.project) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.PROJECT_ID_MISSING,\n 'Google Cloud project ID is required when using Vertex AI. Set GOOGLE_CLOUD_PROJECT environment variable or pass project to constructor',\n );\n }\n\n // Auth initialization handled by AuthManager during connect\n }\n\n /**\n * Register an event listener\n * @param event Event name (e.g., 'speaking', 'writing', 'error', 'speaker')\n * @param callback Callback function that receives event data\n *\n * @example\n * ```typescript\n * // Listen for audio responses\n * voice.on('speaking', ({ audio, audioData, sampleRate }) => {\n * console.log('Received audio chunk:', audioData.length);\n * });\n *\n * // Listen for text responses and transcriptions\n * voice.on('writing', ({ text, role }) => {\n * console.log(`${role}: ${text}`);\n * });\n *\n * // Listen for audio streams (for concatenated playback)\n * voice.on('speaker', (audioStream) => {\n * audioStream.pipe(playbackDevice);\n * });\n *\n * // Handle errors\n * voice.on('error', ({ message, code, details }) => {\n * console.error('Voice error:', message);\n * });\n * ```\n */\n on<E extends VoiceEventType>(\n event: E,\n callback: (data: E extends keyof GeminiLiveEventMap ? GeminiLiveEventMap[E] : unknown) => void,\n ): void {\n try {\n this.eventManager.on(event as GeminiEventName, callback as any);\n this.log(`Event listener registered for: ${event}`);\n } catch (error) {\n this.log(`Failed to register event listener for ${event}:`, error);\n throw error;\n }\n }\n\n /**\n * Remove an event listener\n * @param event Event name\n * @param callback Callback function to remove\n */\n off<E extends VoiceEventType>(\n event: E,\n callback: (data: E extends keyof GeminiLiveEventMap ? GeminiLiveEventMap[E] : unknown) => void,\n ): void {\n try {\n this.eventManager.off(event as GeminiEventName, callback as any);\n this.log(`Event listener removed for: ${event}`);\n } catch (error) {\n this.log(`Failed to remove event listener for ${event}:`, error);\n }\n }\n\n /**\n * Register a one-time event listener that automatically removes itself after the first emission\n * @param event Event name\n * @param callback Callback function that receives event data\n */\n once<E extends VoiceEventType>(\n event: E,\n callback: (data: E extends keyof GeminiLiveEventMap ? GeminiLiveEventMap[E] : unknown) => void,\n ): void {\n try {\n this.eventManager.once(event as GeminiEventName, callback as any);\n this.log(`One-time event listener registered for: ${event}`);\n } catch (error) {\n this.log(`Failed to register one-time event listener for ${event}:`, error);\n throw error;\n }\n }\n\n /**\n * Emit an event to listeners with improved error handling\n * @private\n */\n private emit<K extends keyof GeminiLiveEventMap>(event: K, data: GeminiLiveEventMap[K]): boolean {\n try {\n const listenerCount = this.eventManager.getListenerCount(event as string);\n if (listenerCount === 0 && this.debug) {\n this.log(`No listeners for event: ${String(event)}`);\n }\n\n const result = this.eventManager.emit(event as GeminiEventName, data as any);\n\n if (this.debug && listenerCount > 0) {\n this.log(`Emitted event: ${String(event)} to ${listenerCount} listeners`);\n }\n\n return result;\n } catch (error) {\n this.log(`Error emitting event ${String(event)}:`, error);\n\n // Emit error event if this wasn't already an error event (prevent infinite loops)\n if (event !== 'error') {\n try {\n // Use underlying emitter directly to avoid recursion\n this.eventManager.getEventEmitter().emit('error', {\n message: `Failed to emit event: ${String(event)}`,\n code: 'event_emission_error',\n details: error,\n });\n } catch (nestedError) {\n // If we can't even emit the error event, log it\n this.log('Critical: Failed to emit error event:', nestedError);\n }\n }\n\n return false;\n }\n }\n\n /**\n * Clean up event listeners to prevent memory leaks\n * @private\n */\n private cleanupEventListeners(): void {\n try {\n // Get current listener counts for debugging\n const events = this.eventManager.getEventEmitter().eventNames();\n if (this.debug && events.length > 0) {\n this.log(\n 'Cleaning up event listeners:',\n events.map(event => `${String(event)}: ${this.eventManager.getListenerCount(String(event))}`).join(', '),\n );\n }\n\n // Remove all listeners\n this.eventManager.cleanup();\n\n this.log('Event listeners cleaned up');\n } catch (error) {\n this.log('Error cleaning up event listeners:', error);\n }\n }\n\n /**\n * Get current event listener information for debugging\n * @returns Object with event names and listener counts\n */\n getEventListenerInfo(): Record<string, number> {\n try {\n return this.eventManager.getEventListenerInfo();\n } catch (error) {\n this.log('Error getting event listener info:', error);\n return {} as Record<string, number>;\n }\n }\n\n /**\n * Create and emit a standardized error\n * @private\n */\n private createAndEmitError(code: GeminiLiveErrorCode, message: string, details?: unknown): GeminiLiveError {\n const error = new GeminiLiveError(code, message, details);\n this.log(`Error [${code}]: ${message}`, details);\n this.emit('error', error.toEventData());\n return error;\n }\n\n /**\n * Handle connection state validation with standardized errors\n * @private\n */\n private validateConnectionState(): void {\n if (this.state !== 'connected') {\n throw this.createAndEmitError(\n GeminiLiveErrorCode.NOT_CONNECTED,\n 'Not connected to Gemini Live API. Call connect() first.',\n { currentState: this.state },\n );\n }\n }\n\n /**\n * Handle WebSocket state validation with standardized errors\n * @private\n */\n private validateWebSocketState(): void {\n if (!this.connectionManager.isConnected()) {\n throw this.createAndEmitError(GeminiLiveErrorCode.WEBSOCKET_ERROR, 'WebSocket is not open', {\n wsExists: !!this.connectionManager.getWebSocket(),\n readyState: this.connectionManager.getWebSocket()?.readyState,\n expectedState: WebSocket.OPEN,\n });\n }\n }\n\n /**\n * Establish connection to the Gemini Live API\n */\n async connect({ requestContext }: { requestContext?: any } = {}): Promise<void> {\n if (this.state === 'connected') {\n this.log('Already connected to Gemini Live API');\n return;\n }\n\n // Dedup state must not leak across connections: ids used by a prior\n // connection are valid again once a fresh one is opened.\n this.processedToolCallIds.clear();\n\n // Store request context for tool execution\n this.requestContext = requestContext;\n\n // Emit connecting event\n this.emit('session', { state: 'connecting' });\n\n try {\n // Build WebSocket URL based on official Gemini Live API documentation\n let wsUrl: string;\n let headers: WebSocket.ClientOptions = {};\n\n if (this.options.vertexAI) {\n const location = this.getVertexLocation();\n // Vertex AI endpoint - using correct LlmBidiService endpoint\n wsUrl = `wss://${location}-aiplatform.googleapis.com/ws/google.cloud.aiplatform.v1beta1.LlmBidiService/BidiGenerateContent`;\n // Initialize auth and get token\n await this.authManager.initialize();\n const accessToken = await this.authManager.getAccessToken();\n headers = { headers: { Authorization: `Bearer ${accessToken}` } };\n this.log('Using Vertex AI authentication with OAuth token');\n } else {\n // Live API endpoint - this is specifically for the Live API\n wsUrl = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent`;\n headers = {\n headers: {\n 'x-goog-api-key': this.options.apiKey || '',\n 'Content-Type': 'application/json',\n },\n };\n this.log('Using Live API authentication with API key');\n }\n\n this.log('Connecting to:', wsUrl);\n this.ws = new WebSocket(wsUrl, undefined, headers);\n this.connectionManager.setWebSocket(this.ws);\n\n this.setupEventListeners();\n\n // Wait for WebSocket connection to open via ConnectionManager\n await this.connectionManager.waitForOpen();\n\n // Send initial configuration (session_resumption handle is embedded in setup frame)\n this.sendInitialConfig();\n this.sessionStartTime = Date.now();\n this.sessionId = globalThis.crypto.randomUUID();\n\n // Wait for session to be created after sending config\n await this.waitForSessionCreated();\n\n this.state = 'connected';\n\n // Emit session connected event\n this.emit('session', {\n state: 'connected',\n config: {\n sessionId: this.sessionId,\n isResuming: this.isResuming,\n toolCount: Object.keys(this.tools || {}).length,\n },\n });\n\n this.log('Successfully connected to Gemini Live API', {\n sessionId: this.sessionId,\n isResuming: this.isResuming,\n toolCount: Object.keys(this.tools || {}).length,\n });\n\n // Start session duration monitoring if configured\n if (this.options.sessionConfig?.maxDuration) {\n this.startSessionDurationMonitor();\n }\n } catch (error) {\n this.state = 'disconnected';\n this.log('Connection failed', error);\n throw error;\n }\n }\n\n /**\n * Disconnect from the Gemini Live API\n */\n async disconnect(): Promise<void> {\n if (this.state === 'disconnected') {\n this.log('Already disconnected');\n return;\n }\n\n // Emit disconnecting event\n this.emit('session', { state: 'disconnecting' });\n\n // Clean up session duration monitoring\n if (this.sessionDurationTimeout) {\n clearTimeout(this.sessionDurationTimeout);\n this.sessionDurationTimeout = undefined;\n }\n\n // Session handle is set by the server via sessionResumptionUpdate frames.\n // Do not overwrite it with the client-side sessionId here.\n\n if (this.ws) {\n this.connectionManager.close();\n this.ws = undefined;\n }\n\n // Clean up speaker streams with improved handling\n this.audioStreamManager.cleanupSpeakerStreams();\n\n // Clear cached OAuth token via AuthManager\n this.authManager.clearCache();\n\n this.state = 'disconnected';\n this.isResuming = false;\n\n // Emit final session event before cleanup\n this.emit('session', { state: 'disconnected' });\n\n // Clean up event listeners to prevent memory leaks\n this.cleanupEventListeners();\n\n this.log('Disconnected from Gemini Live API', {\n sessionId: this.sessionId,\n sessionDuration: this.sessionStartTime ? Date.now() - this.sessionStartTime : undefined,\n });\n }\n\n /**\n * Send text to be converted to speech\n */\n async speak(input: string | NodeJS.ReadableStream, options?: GeminiLiveVoiceOptions): Promise<void> {\n this.validateConnectionState();\n\n if (typeof input !== 'string') {\n const chunks: Buffer[] = [];\n for await (const chunk of input) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));\n }\n input = Buffer.concat(chunks).toString('utf-8');\n }\n\n if (input.trim().length === 0) {\n throw this.createAndEmitError(GeminiLiveErrorCode.INVALID_AUDIO_FORMAT, 'Input text is empty');\n }\n\n // Add to context history\n this.addToContext('user', input);\n\n // Build text message to Gemini Live API\n const textMessage: any = {\n client_content: {\n turns: [\n {\n role: 'user',\n parts: [\n {\n text: input,\n },\n ],\n },\n ],\n turnComplete: true,\n },\n };\n\n // If runtime options provided, send a session.update first to apply per-turn settings\n if (options && (options.speaker || options.languageCode || options.responseModalities)) {\n const updateMessage: UpdateMessage = {\n type: 'session.update',\n session: {\n generation_config: {\n ...(options.responseModalities ? { response_modalities: options.responseModalities } : {}),\n speech_config: {\n ...(options.languageCode ? { language_code: options.languageCode } : {}),\n ...(options.speaker ? { voice_config: { prebuilt_voice_config: { voice_name: options.speaker } } } : {}),\n },\n },\n },\n };\n\n try {\n this.sendEvent('session.update', updateMessage);\n this.log('Applied per-turn runtime options', options);\n } catch (error) {\n this.log('Failed to apply per-turn runtime options', error);\n }\n }\n\n try {\n this.sendEvent('client_content', textMessage);\n this.log('Text message sent', { text: input });\n\n // The response will come via the event system (handleServerContent)\n // Audio will be emitted through 'speaking' events\n // Text responses will be emitted through 'writing' events\n } catch (error) {\n this.log('Failed to send text message', error);\n throw this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Failed to send text message', error);\n }\n }\n\n /**\n * Send conversation history into the live session without triggering a model response.\n *\n * Maps to a single Gemini Live `client_content` frame with `turnComplete` defaulting\n * to `false`, which loads the turns into context silently. The model only responds\n * once a subsequent turn completes (e.g. via {@link speak} or user audio).\n *\n * @param turns Prior conversation turns to seed into the session.\n * @param options.turnComplete Whether to mark the turn as complete (default `false`).\n *\n * @example\n * ```typescript\n * await voice.connect();\n *\n * // Replay prior conversation without triggering a reply.\n * await voice.sendContext([\n * { role: 'user', content: 'What is the weather?' },\n * { role: 'assistant', content: 'It is 72°F in San Francisco.' },\n * ]);\n *\n * // Agent stays silent until the user actually speaks.\n * await voice.send(micStream);\n * ```\n */\n async sendContext(turns: IncrementalTurn[], options?: { turnComplete?: boolean }): Promise<void> {\n this.validateConnectionState();\n\n if (!turns || turns.length === 0) {\n this.log('sendContext called with empty turns, skipping');\n return;\n }\n\n const message = {\n client_content: {\n turns: turns.map(t => ({\n role: t.role === 'assistant' ? 'model' : 'user',\n parts: [{ text: t.content }],\n })),\n turnComplete: options?.turnComplete ?? false,\n },\n };\n\n try {\n this.sendEvent('client_content', message);\n this.log('Context seeded', { turnCount: turns.length, turnComplete: options?.turnComplete ?? false });\n\n for (const turn of turns) {\n this.addToContext(turn.role, turn.content);\n }\n } catch (error) {\n this.log('Failed to send context', error);\n throw this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Failed to send context', error);\n }\n }\n\n /**\n * Send audio stream for processing\n */\n async send(audioData: NodeJS.ReadableStream | Int16Array): Promise<void> {\n this.validateConnectionState();\n\n if ('readable' in audioData && typeof audioData.on === 'function') {\n const stream = audioData as NodeJS.ReadableStream;\n\n stream.on('data', (chunk: Buffer) => {\n try {\n const base64Audio = this.audioStreamManager.processAudioChunk(chunk);\n const message = this.audioStreamManager.createAudioMessage(base64Audio, 'realtime');\n this.sendEvent('realtime_input', message);\n } catch (error) {\n this.log('Failed to process audio chunk', error);\n this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Failed to process audio chunk', error);\n }\n });\n\n stream.on('error', (error: Error) => {\n this.log('Audio stream error', error);\n this.createAndEmitError(GeminiLiveErrorCode.AUDIO_STREAM_ERROR, 'Audio stream error', error);\n });\n\n stream.on('end', () => {\n this.log('Audio stream ended');\n });\n } else {\n const validateAudio = this.audioStreamManager.validateAndConvertAudioInput(audioData as Int16Array);\n const base64Audio = this.audioStreamManager.int16ArrayToBase64(validateAudio);\n const message = this.audioStreamManager.createAudioMessage(base64Audio, 'realtime');\n this.sendEvent('realtime_input', message);\n }\n }\n\n /**\n * Process speech from audio stream (traditional STT interface)\n */\n async listen(audioStream: NodeJS.ReadableStream, _options?: GeminiLiveVoiceOptions): Promise<string> {\n this.validateConnectionState();\n\n let transcriptionText = '';\n\n // Listen for transcription responses\n const onWriting = (data: { text: string; role: 'assistant' | 'user' }) => {\n if (data.role === 'user') {\n transcriptionText += data.text;\n this.log('Received transcription text:', { text: data.text, total: transcriptionText });\n }\n // Note: We only collect user role text as transcription\n // Assistant role text would be responses, not transcription\n };\n\n // Listen for errors\n const onError = (error: { message: string; code?: string; details?: unknown }) => {\n throw new Error(`Transcription failed: ${error.message}`);\n };\n\n // Listen for session events\n const onSession = (data: { state: string }) => {\n if (data.state === 'disconnected') {\n throw new Error('Session disconnected during transcription');\n }\n };\n\n // Set up GeminiLiveVoice event listeners\n this.on('writing', onWriting);\n this.on('error', onError);\n this.on('session', onSession);\n\n try {\n // Use AudioStreamManager to handle the transcription workflow\n const result = await this.audioStreamManager.handleAudioTranscription(\n audioStream,\n (base64Audio: string) => {\n // Send audio and await transcript until turn completes\n return new Promise<string>((resolve, reject) => {\n try {\n // Create audio message for transcription\n const message = this.audioStreamManager.createAudioMessage(base64Audio, 'input');\n\n const cleanup = () => {\n this.off('turnComplete' as any, onTurnComplete as any);\n this.off('error', onErr as any);\n };\n\n // Handlers\n const onTurnComplete = () => {\n cleanup();\n resolve(transcriptionText.trim());\n };\n\n const onErr = (e: { message: string }) => {\n cleanup();\n reject(new Error(e.message));\n };\n\n // Wire listeners before sending\n this.on('turnComplete' as any, onTurnComplete as any);\n this.on('error', onErr as any);\n\n // Send to Gemini Live API\n this.sendEvent('client_content', message);\n this.log('Sent audio for transcription');\n } catch (err) {\n reject(err as Error);\n }\n });\n },\n (error: Error) => {\n this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Audio transcription failed', error);\n },\n );\n\n return result;\n } finally {\n // Clean up event listeners\n this.off('writing', onWriting);\n this.off('error', onError);\n this.off('session', onSession);\n }\n }\n\n /**\n * Get available speakers/voices\n */\n async getSpeakers(): Promise<Array<{ voiceId: string; description?: string }>> {\n // Return available Gemini Live voices\n return [\n { voiceId: 'Puck', description: 'Conversational, friendly' },\n { voiceId: 'Charon', description: 'Deep, authoritative' },\n { voiceId: 'Kore', description: 'Neutral, professional' },\n { voiceId: 'Fenrir', description: 'Warm, approachable' },\n ];\n }\n\n /**\n * Resume a previous session using a session handle\n */\n async resumeSession(handle: string, context?: Array<{ role: string; content: string }>): Promise<void> {\n if (this.state === 'connected') {\n throw new Error('Cannot resume session while already connected. Disconnect first.');\n }\n\n this.log('Attempting to resume session', { handle });\n\n this.sessionHandle = handle;\n this.isResuming = true;\n\n // Restore context history if provided using ContextManager\n if (context && context.length > 0) {\n this.contextManager.clearContext();\n for (const item of context) {\n this.contextManager.addEntry(item.role as 'user' | 'assistant', item.content);\n }\n }\n\n try {\n await this.connect();\n this.log('Session resumed successfully', { handle, contextItems: context?.length || 0 });\n } catch (error) {\n this.isResuming = false;\n this.sessionHandle = undefined;\n throw new Error(`Failed to resume session: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Update session configuration during an active session\n * Allows dynamic updates to voice, instructions, tools, and other settings\n *\n * @param config Partial configuration to update\n * @throws Error if not connected or update fails\n *\n * @example\n * ```typescript\n * // Change voice during conversation\n * await voice.updateSessionConfig({\n * speaker: 'Charon'\n * });\n *\n * // Update instructions\n * await voice.updateSessionConfig({\n * instructions: 'You are now a helpful coding assistant'\n * });\n *\n * // Add or update tools\n * await voice.updateSessionConfig({\n * tools: [{ name: 'new_tool', ... }]\n * });\n * ```\n */\n async updateSessionConfig(config: Partial<GeminiLiveVoiceConfig>): Promise<void> {\n this.validateConnectionState();\n this.validateWebSocketState();\n\n return new Promise((resolve, reject) => {\n // Validate configuration\n if (config.model) {\n this.log('Warning: Model cannot be changed during an active session. Ignoring model update.');\n }\n\n if (config.vertexAI !== undefined || config.project !== undefined || config.location !== undefined) {\n this.log('Warning: Authentication settings cannot be changed during an active session.');\n }\n\n const updateMessage: UpdateMessage = {\n type: 'session.update',\n session: {},\n };\n\n let hasUpdates = false;\n\n // Update voice/speaker if provided\n if (config.speaker) {\n hasUpdates = true;\n updateMessage.session.generation_config = {\n ...updateMessage.session.generation_config,\n speech_config: {\n voice_config: {\n prebuilt_voice_config: {\n voice_name: config.speaker,\n },\n },\n },\n };\n\n // Update internal state\n this.speaker = config.speaker;\n this.log('Updating speaker to:', config.speaker);\n }\n\n // Update thinking configuration if provided. Mirrors `sendInitialConfig`: public config is\n // camelCase, the wire format is snake_case. Merge into any existing generation_config so a\n // speaker + thinkingConfig update in the same call don't clobber each other. Only emit when a\n // sub-field is set, so an empty `thinkingConfig: {}` is a no-op rather than a bare object.\n const tc = config.thinkingConfig;\n if (tc && (tc.includeThoughts !== undefined || tc.thinkingBudget !== undefined)) {\n hasUpdates = true;\n updateMessage.session.generation_config = {\n ...updateMessage.session.generation_config,\n thinking_config: {\n ...(tc.includeThoughts !== undefined && { include_thoughts: tc.includeThoughts }),\n ...(tc.thinkingBudget !== undefined && { thinking_budget: tc.thinkingBudget }),\n },\n };\n\n this.options.thinkingConfig = tc;\n this.log('Updating thinkingConfig');\n }\n\n // Update instructions if provided\n if (config.instructions !== undefined) {\n hasUpdates = true;\n updateMessage.session.system_instruction = {\n parts: [{ text: config.instructions }],\n };\n\n this.log('Updating instructions');\n }\n\n // Mirror `sendInitialConfig`: flatten both tool sources — the explicit `config.tools` arg\n // and the `addTools()` registry — into a single `function_declarations` container so the\n // model receives every available tool. The multi-container shape previously emitted here\n // was accepted by Gemini at setup but suppressed tool_call frames mid-session, reintroducing #17018.\n const hasRegisteredTools = !!this.tools && Object.keys(this.tools).length > 0;\n // `config.tools: []` is the explicit-clear signal; honor it even when the `addTools()`\n // registry is non-empty, otherwise the caller has no way to remove all tools mid-session.\n const isExplicitClear = Array.isArray(config.tools) && config.tools.length === 0;\n if (config.tools !== undefined || hasRegisteredTools) {\n hasUpdates = true;\n const declarations = isExplicitClear ? [] : this.buildToolDeclarations(config.tools, this.tools);\n if (declarations.length > 0) {\n updateMessage.session.tools = [{ function_declarations: declarations }];\n } else {\n updateMessage.session.tools = [];\n }\n this.log('Updating tools:', declarations.length, 'tools');\n }\n\n // Update session configuration if provided\n if (config.sessionConfig) {\n // Handle VAD settings\n if (config.sessionConfig.vad) {\n hasUpdates = true;\n updateMessage.session.vad = {\n enabled: config.sessionConfig.vad.enabled ?? true,\n sensitivity: config.sessionConfig.vad.sensitivity ?? 0.5,\n silence_duration_ms: config.sessionConfig.vad.silenceDurationMs ?? 1000,\n };\n this.log('Updating VAD settings:', config.sessionConfig.vad);\n }\n\n // Handle interrupt settings\n if (config.sessionConfig.interrupts) {\n hasUpdates = true;\n updateMessage.session.interrupts = {\n enabled: config.sessionConfig.interrupts.enabled ?? true,\n allow_user_interruption: config.sessionConfig.interrupts.allowUserInterruption ?? true,\n };\n this.log('Updating interrupt settings:', config.sessionConfig.interrupts);\n }\n\n // Handle context compression\n if (config.sessionConfig.contextCompression !== undefined) {\n hasUpdates = true;\n updateMessage.session.context_compression = config.sessionConfig.contextCompression;\n this.log('Updating context compression:', config.sessionConfig.contextCompression);\n // Apply to ContextManager\n this.contextManager.setCompressionEnabled(config.sessionConfig.contextCompression);\n }\n }\n\n // Check if there are any updates to send\n if (!hasUpdates) {\n this.log('No valid configuration updates to send');\n resolve();\n return;\n }\n\n // Set up timeout for response\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Session configuration update timeout - no response received'));\n }, 10000); // 10 second timeout\n\n // Listen for update confirmation\n const onSessionUpdated = (data: GeminiLiveServerMessage) => {\n cleanup();\n this.log('Session configuration updated successfully', data);\n resolve();\n };\n\n // Listen for errors\n const onError = (error: { message?: string; code?: string; details?: unknown }) => {\n cleanup();\n this.log('Session configuration update failed', error);\n reject(new Error(`Failed to update session configuration: ${error.message || 'Unknown error'}`));\n };\n\n // Set up event listeners\n const cleanup = () => {\n clearTimeout(timeout);\n this.eventManager.getEventEmitter().removeListener('session.updated', onSessionUpdated as any);\n this.eventManager.getEventEmitter().removeListener('error', onError as any);\n };\n\n this.eventManager.getEventEmitter().once('session.updated', onSessionUpdated as any);\n this.eventManager.getEventEmitter().once('error', onError as any);\n\n // Send the update message\n try {\n this.sendEvent('session.update', updateMessage);\n this.log('Sent session configuration update', updateMessage);\n } catch (error) {\n cleanup();\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n this.log('Failed to send session configuration update', error);\n reject(new Error(`Failed to send session configuration update: ${errorMessage}`));\n }\n });\n }\n\n /**\n * Get current connection state\n */\n getConnectionState(): 'disconnected' | 'connected' {\n return this.state;\n }\n\n /**\n * Check if currently connected\n */\n isConnected(): boolean {\n return this.state === 'connected';\n }\n\n /**\n * Get current speaker stream for audio concatenation\n * This allows external access to the current audio stream being built\n */\n getCurrentSpeakerStream(): NodeJS.ReadableStream | null {\n return this.audioStreamManager.getCurrentSpeakerStream();\n }\n\n /**\n * Get session handle for resumption\n */\n getSessionHandle(): string | undefined {\n // TODO: Return actual session handle when Gemini Live API supports session resumption\n return this.sessionHandle;\n }\n\n /**\n * Get comprehensive session information\n */\n getSessionInfo(): {\n id?: string;\n handle?: string;\n startTime?: Date;\n duration?: number;\n state: string;\n config?: GeminiSessionConfig;\n contextSize: number;\n } {\n return {\n id: this.sessionId,\n handle: this.sessionHandle,\n startTime: this.sessionStartTime ? new Date(this.sessionStartTime) : undefined,\n duration: this.sessionStartTime ? Date.now() - this.sessionStartTime : undefined,\n state: this.state,\n config: this.options.sessionConfig,\n contextSize: this.contextManager.getContextSize(),\n };\n }\n\n /**\n * Get session context history\n */\n getContextHistory(): Array<{ role: string; content: string; timestamp: number }> {\n return this.contextManager.getContextHistory();\n }\n\n /**\n * Add to context history for session continuity\n */\n addToContext(role: 'user' | 'assistant', content: string): void {\n this.contextManager.addEntry(role, content);\n }\n\n /**\n * Clear session context\n */\n clearContext(): void {\n this.contextManager.clearContext();\n this.log('Session context cleared');\n }\n\n /**\n * Enable or disable automatic reconnection\n */\n setAutoReconnect(enabled: boolean): void {\n if (!this.options.sessionConfig) {\n this.options.sessionConfig = {};\n }\n this.options.sessionConfig.enableResumption = enabled;\n this.log(`Auto-reconnect ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /**\n * Send session resumption message\n * @private\n */\n /**\n * Start monitoring session duration\n * @private\n */\n private startSessionDurationMonitor(): void {\n if (!this.options.sessionConfig?.maxDuration) {\n return;\n }\n\n // Parse duration string (e.g., '24h', '2h', '30m')\n const durationMs = this.parseDuration(this.options.sessionConfig.maxDuration);\n\n if (!durationMs) {\n this.log('Invalid session duration format', { duration: this.options.sessionConfig.maxDuration });\n return;\n }\n\n // Clear existing monitor if any\n if (this.sessionDurationTimeout) {\n clearTimeout(this.sessionDurationTimeout);\n }\n\n // Set timeout for session expiry warning\n const warningTime = durationMs - 5 * 60 * 1000; // 5 minutes before expiry\n\n if (warningTime > 0) {\n setTimeout(() => {\n this.emit('sessionExpiring', {\n expiresIn: 5 * 60 * 1000,\n sessionId: this.sessionId,\n });\n }, warningTime);\n }\n\n // Set timeout for session expiry\n this.sessionDurationTimeout = setTimeout(() => {\n this.log('Session duration limit reached, disconnecting');\n void this.disconnect();\n }, durationMs);\n }\n\n /**\n * Parse duration string to milliseconds\n * @private\n */\n private parseDuration(duration: string): number | null {\n const match = duration.match(/^(\\d+)([hms])$/);\n if (!match) return null;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2];\n\n switch (unit) {\n case 'h':\n return value * 60 * 60 * 1000;\n case 'm':\n return value * 60 * 1000;\n case 's':\n return value * 1000;\n default:\n return null;\n }\n }\n\n /**\n * Compress context history to manage memory\n * @private\n */\n private compressContext(): void {\n // Deprecated: ContextManager handles compression\n this.log('compressContext is deprecated; handled by ContextManager');\n }\n\n /**\n * Setup WebSocket event listeners for Gemini Live API messages\n * @private\n */\n private setupEventListeners(): void {\n if (!this.ws) {\n throw new Error('WebSocket not initialized');\n }\n\n // Handle WebSocket connection events\n this.ws.on('open', () => {\n this.log('WebSocket connection opened');\n // Note: We transition to 'connected' in connect() after setup is complete\n // This is just confirming the WebSocket is open\n });\n\n this.ws.on('close', (code: number, reason: Buffer) => {\n const reasonText = reason.toString();\n this.log('WebSocket connection closed', { code, reason: reasonText });\n this.state = 'disconnected';\n this.emit('session', { state: 'disconnected', code, reason: reasonText });\n\n // A clean server-initiated close (e.g. 1007 invalid-argument for a bad model id or a\n // malformed setup frame) arrives as `close`, not as a socket `error`. Surface it as an\n // `error` so a pending connect() (waitForSessionCreated) rejects immediately with the\n // real close code and reason instead of waiting out the 30s setup timeout.\n if (code !== 1000) {\n this.emit('error', {\n message: `WebSocket closed during/after setup (code ${code})${reasonText ? `: ${reasonText}` : ''}`,\n code: 'websocket_closed',\n details: { code, reason: reasonText },\n });\n }\n });\n\n this.ws.on('error', (error: Error) => {\n this.log('WebSocket error', error);\n this.state = 'disconnected';\n this.emit('session', { state: 'disconnected' });\n this.emit('error', {\n message: error.message,\n code: 'websocket_error',\n details: error,\n });\n });\n\n // Handle incoming messages from Gemini Live API\n this.ws.on('message', async (message: Buffer | string) => {\n try {\n const data = JSON.parse(message.toString());\n await this.handleGeminiMessage(data);\n } catch (error) {\n this.log('Failed to parse WebSocket message', error);\n this.emit('error', {\n message: 'Failed to parse WebSocket message',\n code: 'parse_error',\n details: error,\n });\n }\n });\n }\n\n /**\n * Handle different types of messages from Gemini Live API\n * @private\n */\n private async handleGeminiMessage(data: GeminiLiveServerMessage): Promise<void> {\n // Always log received messages in debug mode or when troubleshooting\n this.log('Received message:', JSON.stringify(data, null, 2));\n\n // Extract response ID if present in the message\n if ((data as any).responseId) {\n this.setCurrentResponseId((data as any).responseId);\n this.log('Set current response ID:', (data as any).responseId);\n }\n\n // Handle different Gemini Live API message structures\n if (data.setup) {\n this.log('Processing setup message');\n this.handleSetupComplete(data);\n } else if (data.setupComplete) {\n this.log('Processing setupComplete message');\n this.handleSetupComplete(data);\n } else if (data.serverContent) {\n this.log('Processing server content message');\n this.handleServerContent(data.serverContent);\n } else if (data.toolCall) {\n this.log('Processing tool call message');\n await this.handleToolCall(data);\n }\n\n // Usage metadata and session resumption updates can accompany any primary message.\n // Handle them independently so content, setup, and tool frames do not suppress them.\n if (data.usageMetadata) {\n this.log('Processing usage metadata message');\n this.handleUsageUpdate(data);\n }\n if (data.sessionResumptionUpdate) {\n this.log('Processing session resumption update', data.sessionResumptionUpdate);\n if (data.sessionResumptionUpdate.resumable && data.sessionResumptionUpdate.newHandle) {\n this.sessionHandle = data.sessionResumptionUpdate.newHandle;\n this.log('Session handle updated from server', { handle: this.sessionHandle });\n this.emit('sessionHandle', {\n handle: this.sessionHandle,\n expiresAt: new Date(Date.now() + 2 * 60 * 60 * 1000), // 2h TTL for AI Studio\n });\n }\n } else if (data.sessionEnd) {\n this.log('Processing session end message');\n this.handleSessionEnd(data);\n } else if (data.error) {\n this.log('Processing error message');\n this.handleError(data.error);\n } else {\n // Handle alternative message formats by checking for common fields\n const messageData = data as any; // Use any for flexible message handling\n\n // Check for various possible setup completion indicators\n if (messageData.type === 'setup' || messageData.type === 'session.ready' || messageData.type === 'ready') {\n // Handle alternative setup message formats\n this.log('Processing alternative setup message with type:', messageData.type);\n this.handleSetupComplete(data);\n } else if (messageData.sessionHandle) {\n // Handle session handle in response\n this.log('Processing session handle message');\n this.handleSetupComplete(data);\n } else if (\n messageData.session ||\n messageData.ready ||\n messageData.status === 'ready' ||\n messageData.status === 'setup_complete'\n ) {\n // Try to handle as setup completion if it has any setup-related fields\n this.log('Processing setup completion message with status:', messageData.status);\n this.handleSetupComplete(data);\n } else if (messageData.candidates || messageData.promptFeedback) {\n // Handle successful response from BidiGenerateContent\n this.log('Processing BidiGenerateContent response');\n this.handleSetupComplete(data);\n } else if (messageData.contents && Array.isArray(messageData.contents)) {\n // Handle content response\n this.log('Processing content response');\n this.handleServerContent({ modelTurn: { parts: messageData.contents.flatMap((c: any) => c.parts || []) } });\n // Also treat this as setup completion since we got a response\n this.handleSetupComplete(data);\n } else if (messageData.candidates && Array.isArray(messageData.candidates)) {\n // Handle candidates response (common in Gemini API)\n this.log('Processing candidates response');\n this.handleSetupComplete(data);\n } else {\n this.log('Unknown message format - no recognized fields found');\n }\n }\n }\n\n /**\n * Handle setup completion message\n * @private\n */\n private handleSetupComplete(data: GeminiLiveServerMessage): void {\n this.log('Setup completed');\n\n // Process all queued messages now that the session is ready\n const queue = this.queue.splice(0, this.queue.length);\n if (queue.length > 0) {\n this.log('Processing queued messages:', queue.length);\n for (const queuedMessage of queue) {\n try {\n this.connectionManager.send(JSON.stringify(queuedMessage));\n this.log('Sent queued message:', queuedMessage);\n } catch (err) {\n this.log('Failed to send queued message, re-queuing:', err);\n this.queue.unshift(queuedMessage);\n break;\n }\n }\n }\n\n // Emit event for waitForSessionCreated to resolve\n this.eventManager.getEventEmitter().emit('setupComplete', data as any);\n // Session is now ready for communication\n }\n\n /**\n * Handle session update confirmation\n * @private\n */\n private handleSessionUpdated(data: GeminiLiveServerMessage): void {\n this.log('Session updated', data);\n // Emit event for updateSessionConfig to resolve\n this.eventManager.getEventEmitter().emit('session.updated', data as any);\n\n // Also emit a general session event for any external listeners\n this.emit('session', {\n state: 'updated',\n config: data as Record<string, unknown>,\n });\n }\n\n /**\n * Handle server content (text/audio responses)\n * @private\n */\n private handleServerContent(data: GeminiLiveServerMessage['serverContent']): void {\n if (!data) {\n return;\n }\n\n // Barge-in: the server cancelled the in-flight model response because the\n // user started speaking. Surface this as the `interrupt` event so consumers\n // can drop queued TTS audio. Matches the `interrupt` shape emitted by\n // `@mastra/voice-aws-nova-sonic`.\n //\n // The cancelled turn will not necessarily be followed by `turnComplete`, so\n // end any in-flight speaker streams here. Otherwise stream counters never\n // decrement and downstream playback hangs on the cancelled audio. Discard\n // the partial assistant text — it does not represent a completed turn.\n if (data.interrupted) {\n this.log('Model response interrupted by user activity');\n this.audioStreamManager.cleanupSpeakerStreams();\n this.pendingAssistantResponse = '';\n this.emit('interrupt', { type: 'user', timestamp: Date.now() });\n }\n\n // User-side transcription. Emitted as `writing` with `role: 'user'`,\n // matching the OpenAI / xAI / Inworld realtime pattern of using a single\n // `writing` channel with role disambiguation.\n if (data.inputTranscription?.text) {\n this.emit('writing', {\n text: data.inputTranscription.text,\n role: 'user',\n });\n }\n\n // Model-side transcription. On native-audio models this is the\n // authoritative source for the spoken response — emit it as `writing` with\n // `role: 'assistant'`. On non-native-audio models this field is not sent\n // (the spoken response comes from `modelTurn.parts.text` below).\n if (data.outputTranscription?.text) {\n this.pendingAssistantResponse += data.outputTranscription.text;\n this.emit('writing', {\n text: data.outputTranscription.text,\n role: 'assistant',\n });\n }\n\n const nativeAudio = this.isNativeAudioModel();\n\n if (data.modelTurn?.parts) {\n for (const part of data.modelTurn.parts) {\n // Handle text content. Native-audio models put their internal reasoning\n // here while the spoken response goes through `outputTranscription`\n // above — route reasoning to the Gemini-specific `thinking` event so\n // consumers can render it separately without conflating it with the\n // assistant's actual reply. Non-native-audio models do not send\n // `outputTranscription`, so `part.text` IS the spoken response and\n // continues to flow through `writing`.\n if (part.text) {\n if (nativeAudio) {\n this.emit('thinking', { text: part.text });\n } else {\n this.pendingAssistantResponse += part.text;\n this.emit('writing', {\n text: part.text,\n role: 'assistant',\n });\n }\n }\n\n // Handle function calls (tool calls) embedded in parts\n // Gemini Live API sends tool calls inside serverContent.modelTurn.parts\n if (part.functionCall) {\n this.log('Found function call in serverContent.modelTurn.parts', part.functionCall);\n\n // Convert to toolCall format and handle\n const toolCallData: GeminiLiveServerMessage = {\n toolCall: {\n name: part.functionCall.name,\n args: part.functionCall.args || {},\n id: part.functionCall.id || globalThis.crypto.randomUUID(),\n },\n };\n\n // Handle the tool call asynchronously\n void this.handleToolCall(toolCallData);\n\n // Continue to next part without processing audio/text\n continue;\n }\n\n // Handle audio content - implement chunk concatenation with proper response ID tracking\n if (part.inlineData?.mimeType?.includes('audio') && typeof part.inlineData.data === 'string') {\n try {\n const audioData = part.inlineData.data;\n const int16Array = this.audioStreamManager.base64ToInt16Array(audioData);\n\n // Use the tracked response ID or generate one if not available\n const responseId = this.getCurrentResponseId() || globalThis.crypto.randomUUID();\n\n // Get or create the speaker stream for this response\n let speakerStream = this.audioStreamManager.getSpeakerStream(responseId);\n if (!speakerStream) {\n // Clean up stale streams and enforce limits before creating new ones\n this.audioStreamManager.cleanupStaleStreams();\n this.audioStreamManager.enforceStreamLimits();\n\n // Create new stream through the manager\n speakerStream = this.audioStreamManager.createSpeakerStream(responseId);\n\n // Add error handling to the stream\n speakerStream.on('error', (streamError: Error) => {\n this.log(`Speaker stream error for ${responseId}:`, streamError);\n this.audioStreamManager.removeSpeakerStream(responseId);\n this.emit('error', {\n message: 'Speaker stream error',\n code: 'speaker_stream_error',\n details: { responseId, error: streamError },\n });\n });\n\n // Auto-cleanup when stream ends\n speakerStream.on('end', () => {\n this.log(`Speaker stream ended for response: ${responseId}`);\n this.audioStreamManager.removeSpeakerStream(responseId);\n });\n\n // Auto-cleanup when stream is destroyed\n speakerStream.on('close', () => {\n this.log(`Speaker stream closed for response: ${responseId}`);\n this.audioStreamManager.removeSpeakerStream(responseId);\n });\n\n this.log('Created new speaker stream for response:', responseId);\n\n // Emit the speaker stream for external listeners\n this.emit('speaker', speakerStream as NodeJS.ReadableStream);\n }\n\n // Write the audio chunk to the stream\n const audioBuffer = Buffer.from(int16Array.buffer, int16Array.byteOffset, int16Array.byteLength);\n speakerStream.write(audioBuffer);\n\n this.log('Wrote audio chunk to stream:', {\n responseId,\n chunkSize: audioBuffer.length,\n totalStreams: this.audioStreamManager.getActiveStreamCount(),\n });\n\n // Also emit the individual speaking event for backward compatibility\n this.emit('speaking', {\n audio: audioData, // Base64 string\n audioData: int16Array,\n sampleRate: this.audioConfig.outputSampleRate, // Gemini Live outputs at 24kHz\n });\n } catch (error) {\n this.log('Error processing audio data:', error);\n this.emit('error', {\n message: 'Failed to process audio data',\n code: 'audio_processing_error',\n details: error,\n });\n }\n }\n }\n }\n\n // Check for turn completion\n if (data.turnComplete) {\n this.log('Turn completed');\n\n // Flush the assistant text accumulated across this turn's `serverContent`\n // frames as a single context entry. Doing this once per turn (rather than\n // once per frame) keeps the conversation history coherent even when the\n // Live API streams a response over many incremental frames.\n if (this.pendingAssistantResponse.trim()) {\n this.addToContext('assistant', this.pendingAssistantResponse);\n }\n this.pendingAssistantResponse = '';\n\n // End all active speaker streams for this turn\n this.audioStreamManager.cleanupSpeakerStreams();\n\n // Emit turn completion event\n this.emit('turnComplete', {\n timestamp: Date.now(),\n });\n }\n }\n\n /**\n * Handle tool call requests from the model\n * @private\n */\n private async handleToolCall(data: GeminiLiveServerMessage): Promise<void> {\n if (!data.toolCall) {\n return;\n }\n\n // Handle both formats:\n // 1. Direct format: { toolCall: { name, args, id } }\n // 2. Array format: { toolCall: { functionCalls: [{ name, args, id }] } }\n let toolCalls: Array<{ name?: string; args?: Record<string, any>; id?: string }> = [];\n\n if (data.toolCall.functionCalls && Array.isArray(data.toolCall.functionCalls)) {\n // Array format (actual Gemini API format)\n toolCalls = data.toolCall.functionCalls;\n } else if (data.toolCall.name) {\n // Direct format (for backward compatibility)\n toolCalls = [{ name: data.toolCall.name, args: data.toolCall.args, id: data.toolCall.id }];\n }\n\n // Process each tool call\n for (const toolCall of toolCalls) {\n const toolName = toolCall.name || '';\n const toolArgs = toolCall.args || {};\n const toolId = toolCall.id || globalThis.crypto.randomUUID();\n\n await this.processSingleToolCall(toolName, toolArgs, toolId);\n }\n }\n\n /**\n * Process a single tool call\n * @private\n */\n private async processSingleToolCall(toolName: string, toolArgs: Record<string, any>, toolId: string): Promise<void> {\n this.log('Processing tool call', { toolName, toolArgs, toolId });\n\n // Skip duplicates: the same call id may arrive through both inbound\n // representations (top-level `toolCall` and\n // `serverContent.modelTurn.parts[].functionCall`).\n if (this.processedToolCallIds.has(toolId)) {\n this.log('Duplicate tool call ignored', { toolName, toolId });\n return;\n }\n this.processedToolCallIds.add(toolId);\n if (this.processedToolCallIds.size > MAX_TRACKED_TOOL_CALL_IDS) {\n const oldest = this.processedToolCallIds.values().next().value;\n if (oldest !== undefined) {\n this.processedToolCallIds.delete(oldest);\n }\n }\n\n // Emit tool call event\n this.emit('toolCall', {\n name: toolName,\n args: toolArgs,\n id: toolId,\n });\n\n // Find the tool\n const tool = this.tools?.[toolName];\n if (!tool) {\n this.log('Tool not found', { toolName });\n\n // Gemini blocks the turn until every `functionCall` id in the batch is answered. Returning\n // without a `functionResponse` leaves the call unanswered forever, so the model never speaks\n // again (dead air until hangup). Send an error response for this id before emitting.\n const notFoundMessage = {\n toolResponse: {\n functionResponses: [\n {\n id: toolId,\n name: toolName,\n response: { error: `Tool \"${toolName}\" not found` },\n },\n ],\n },\n };\n\n this.sendEvent('toolResponse', notFoundMessage);\n\n this.createAndEmitError(GeminiLiveErrorCode.TOOL_NOT_FOUND, `Tool \"${toolName}\" not found`, {\n toolName,\n availableTools: Object.keys(this.tools || {}),\n });\n return;\n }\n\n try {\n // Execute the tool\n let result: unknown;\n\n if (tool.execute) {\n this.log('Executing tool', { toolName, toolArgs });\n\n // Execute with proper context\n result = await tool.execute(toolArgs, { requestContext: this.requestContext });\n\n this.log('Tool executed successfully', { toolName, result });\n } else {\n this.log('Tool has no execute function', { toolName });\n result = { error: 'Tool has no execute function' };\n }\n\n // Gemini Live's `response` proto field is a struct, not repeating. Tools returning arrays\n // or primitives close the session with `1007 Unknown name \"response\": Proto field is not\n // repeating`. Wrap everything except plain objects in `{ result }` so the field is always a\n // struct — `Date`, `Map`, `Set`, `Error`, and class instances all serialize as `{}` if sent\n // bare (their own enumerable properties are empty), losing the tool's data silently.\n const responsePayload = isPlainObject(result) ? result : { result };\n\n // Send tool result back to Gemini Live API\n const toolResultMessage = {\n toolResponse: {\n functionResponses: [\n {\n id: toolId,\n name: toolName,\n response: responsePayload,\n },\n ],\n },\n };\n\n this.sendEvent('toolResponse', toolResultMessage);\n this.log('Tool result sent', { toolName, toolId, result });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n this.log('Tool execution failed', { toolName, error: errorMessage });\n\n // Send error result back to Gemini Live API\n const errorResultMessage = {\n toolResponse: {\n functionResponses: [\n {\n id: toolId,\n name: toolName,\n response: { error: errorMessage },\n },\n ],\n },\n };\n\n this.sendEvent('toolResponse', errorResultMessage);\n\n // Emit error event\n this.createAndEmitError(GeminiLiveErrorCode.TOOL_EXECUTION_ERROR, `Tool execution failed: ${errorMessage}`, {\n toolName,\n toolArgs,\n error,\n });\n }\n }\n\n /**\n * Handle token usage information\n * @private\n */\n private handleUsageUpdate(data: GeminiLiveServerMessage): void {\n if (data.usageMetadata) {\n this.emit('usage', {\n inputTokens: data.usageMetadata.promptTokenCount || 0,\n outputTokens: data.usageMetadata.responseTokenCount || 0,\n totalTokens: data.usageMetadata.totalTokenCount || 0,\n modality: this.determineModality(data),\n });\n }\n }\n\n /**\n * Handle session end\n * @private\n */\n private handleSessionEnd(data: GeminiLiveServerMessage): void {\n this.log('Session ended', data.sessionEnd?.reason);\n this.state = 'disconnected';\n this.emit('session', { state: 'disconnected' });\n }\n\n /**\n * Handle errors\n * @private\n */\n private handleError(error: GeminiLiveServerMessage['error']): void {\n if (!error) {\n this.log('Received error from Gemini Live API (no error details)');\n return;\n }\n\n this.log('Received error from Gemini Live API', error);\n this.emit('error', {\n message: error.message || 'Unknown error',\n code: error.code || 'unknown_error',\n details: error.details,\n });\n }\n\n /**\n * Determine the modality from message data\n * @private\n */\n private determineModality(data: GeminiLiveServerMessage): 'audio' | 'text' | 'video' {\n // Simple heuristic - this could be more sophisticated\n if (data.serverContent?.modelTurn?.parts?.some(part => part.inlineData?.mimeType?.includes('audio'))) {\n return 'audio';\n }\n // Support for video is not yet implemented, leaving this here for future use if needed\n if (data.serverContent?.modelTurn?.parts?.some(part => part.inlineData?.mimeType?.includes('video'))) {\n return 'video';\n }\n return 'text';\n }\n\n /**\n * Resolve Vertex AI location with sensible default\n * @private\n */\n private getVertexLocation(): string {\n return this.options.location?.trim() || 'us-central1';\n }\n\n /**\n * Whether the active model is a Gemini Live \"native-audio\" variant.\n *\n * Native-audio models emit a different `serverContent.modelTurn.parts.text`\n * stream than their half-cascade siblings: on native-audio, that text is the\n * model's internal reasoning (chain-of-thought), and the *spoken* response\n * arrives separately via `serverContent.outputTranscription.text`. On\n * non-native-audio models there is no `outputTranscription` channel, and\n * `modelTurn.parts.text` is the spoken response.\n *\n * Used to decide whether `modelTurn.parts.text` should be emitted as\n * `thinking` (native-audio) or `writing` (non-native-audio). All native-audio\n * model IDs in `GeminiVoiceModel` contain the literal substring\n * `native-audio`, so a substring check is sufficient and forward-compatible\n * with new variants that follow the same naming convention.\n * @private\n */\n private isNativeAudioModel(): boolean {\n const model = this.options.model ?? DEFAULT_MODEL;\n return model.includes('native-audio');\n }\n\n /**\n * Resolve the correct model identifier for Gemini API or Vertex AI\n * @private\n */\n private resolveModelIdentifier(): string {\n const model = this.options.model ?? DEFAULT_MODEL;\n\n if (!this.options.vertexAI) {\n return `models/${model}`;\n }\n\n if (!this.options.project) {\n throw this.createAndEmitError(\n GeminiLiveErrorCode.PROJECT_ID_MISSING,\n 'Google Cloud project ID is required when using Vertex AI.',\n );\n }\n\n const location = this.getVertexLocation();\n return `projects/${this.options.project}/locations/${location}/publishers/google/models/${model}`;\n }\n\n /**\n * Send initial configuration to Gemini Live API\n * @private\n */\n private sendInitialConfig(): void {\n if (!this.ws || !this.connectionManager.isConnected()) {\n throw new Error('WebSocket not connected');\n }\n\n // Live API setup message. Keys must be snake_case to match Gemini Live's wire format —\n // camelCase keys cause native-audio models to reject the setup with\n // `1007 Cannot extract voices from a non-audio request`. Matches the `UpdateMessage` shape\n // already used by this package's `session.update` path.\n interface LiveGenerateContentSetup {\n model?: string;\n generation_config?: {\n temperature?: number;\n top_k?: number;\n top_p?: number;\n max_output_tokens?: number;\n stop_sequences?: string[];\n candidate_count?: number;\n response_modalities?: ('AUDIO' | 'TEXT')[];\n speech_config?: {\n voice_config?: {\n prebuilt_voice_config?: {\n voice_name?: string;\n };\n };\n };\n thinking_config?: {\n include_thoughts?: boolean;\n thinking_budget?: number;\n };\n };\n system_instruction?: {\n parts: Array<{\n text: string;\n }>;\n };\n tools?: Array<{\n function_declarations: Array<{\n name: string;\n description?: string;\n parameters?: unknown;\n }>;\n }>;\n /**\n * Empty-object flag that enables server-side ASR for the user's spoken\n * input. When set, the server emits `serverContent.inputTranscription.text`\n * frames alongside audio. Required to surface what the user said.\n */\n input_audio_transcription?: Record<string, never>;\n /**\n * Empty-object flag that enables server-side transcription of the model's\n * spoken output. When set, the server emits\n * `serverContent.outputTranscription.text` frames. Required on\n * native-audio models — without this, the spoken response text is never\n * delivered to the client (`modelTurn.parts.text` on native-audio is\n * reasoning, not spoken content).\n */\n output_audio_transcription?: Record<string, never>;\n /**\n * Activity handling tells the server how to react to new user activity\n * while the model is still responding. `START_OF_ACTIVITY_INTERRUPTS`\n * cancels the in-flight model response on barge-in and sets\n * `serverContent.interrupted = true`, which is the signal consumers need\n * to drop queued TTS audio.\n */\n realtime_input_config?: {\n activity_handling?: 'START_OF_ACTIVITY_INTERRUPTS' | 'NO_INTERRUPTION';\n };\n /**\n * Session resumption config. Empty object enables server-issued tokens;\n * { handle } resumes a previous session.\n */\n session_resumption?: { handle?: string };\n /**\n * Opts the session into seeding initial conversation history via send_client_content frames.\n * Must be present in the setup frame for sendContext() to work on gemini-3.1-flash-live-preview\n * and later 3.x models — without it the server closes the WebSocket with code 1007.\n */\n history_config?: {\n initial_history_in_client_content?: boolean;\n };\n }\n\n // Native-audio models require `response_modalities: [\"AUDIO\"]` at setup time. This is a voice\n // library, so AUDIO is the only sensible session-level default; callers needing a TEXT-only\n // turn can override per-turn via `GeminiLiveVoiceOptions.responseModalities` on `speak()`.\n const generationConfig: NonNullable<LiveGenerateContentSetup['generation_config']> = {\n response_modalities: ['AUDIO'],\n };\n\n // Only attach `voice_config` when the caller supplied a `speaker`. Omitting the field lets\n // Gemini Live pick its server-side default and avoids pinning a Mastra-side preference.\n if (this.options.speaker) {\n generationConfig.speech_config = {\n voice_config: {\n prebuilt_voice_config: {\n voice_name: this.options.speaker,\n },\n },\n };\n }\n\n // Forward caller-supplied thinking configuration. Public config is camelCase; the wire\n // format is snake_case (`thinking_config.include_thoughts` / `thinking_budget`), matching\n // the translation done for `speech_config` above. Only emit `thinking_config` when at least\n // one sub-field is set, so an empty `thinkingConfig: {}` is a no-op and the setup frame stays\n // byte-for-byte unchanged — a bare `thinking_config: {}` may be rejected by non-thinking models.\n const setupThinking = this.options.thinkingConfig;\n if (setupThinking && (setupThinking.includeThoughts !== undefined || setupThinking.thinkingBudget !== undefined)) {\n generationConfig.thinking_config = {\n ...(setupThinking.includeThoughts !== undefined && { include_thoughts: setupThinking.includeThoughts }),\n ...(setupThinking.thinkingBudget !== undefined && { thinking_budget: setupThinking.thinkingBudget }),\n };\n }\n\n // Build the Live API setup message\n const setupMessage: { setup: LiveGenerateContentSetup } = {\n setup: {\n model: this.resolveModelIdentifier(),\n generation_config: generationConfig,\n // Transcription is on by default — matches the pattern in @mastra/voice-openai-realtime,\n // @mastra/voice-xai-realtime, and @mastra/voice-inworld where realtime sessions\n // unconditionally enable STT in `connect()`. On native-audio models this is the ONLY way\n // to receive the spoken response as text (`modelTurn.parts.text` carries reasoning, not\n // speech), so without these flags the assistant's words are silently dropped client-side.\n input_audio_transcription: {},\n output_audio_transcription: {},\n // Activity-based interrupts surface barge-in as `serverContent.interrupted = true` and\n // cancel the in-flight model response. This is the only way to wire up the `interrupt`\n // event declared in `GeminiLiveEventMap`.\n realtime_input_config: {\n activity_handling: 'START_OF_ACTIVITY_INTERRUPTS',\n },\n // Session resumption: empty object requests server-issued tokens on new sessions;\n // { handle } resumes a previous session. Only included when enableResumption is set.\n ...(this.options.sessionConfig?.enableResumption && {\n session_resumption: this.isResuming && this.sessionHandle ? { handle: this.sessionHandle } : {},\n }),\n // history_config opts the session into send_client_content-based history seeding.\n // Required by gemini-3.1-flash-live-preview (and later 3.x models) for sendContext() to\n // succeed — without this field in the setup frame the server closes the WebSocket with\n // code 1007 (\"Request contains an invalid argument.\"). Defaults to true so that\n // sendContext() works out of the box; set initialHistoryInClientContent: false on\n // sessionConfig to opt out explicitly.\n ...(this.options.sessionConfig?.initialHistoryInClientContent !== false && {\n history_config: {\n initial_history_in_client_content: true,\n },\n }),\n },\n };\n\n // Add system instructions if provided\n if (this.options.instructions) {\n setupMessage.setup.system_instruction = {\n parts: [{ text: this.options.instructions }],\n };\n }\n\n // Gemini Live expects a single `tools` entry whose `function_declarations` array holds every\n // tool. The previous shape (one entry per tool, camelCase `functionDeclarations`) was accepted\n // at setup but the model never emitted tool_call frames back to the client.\n const functionDeclarations = this.buildToolDeclarations(this.options.tools, this.tools);\n\n // Emit tools as a single container with all declarations\n if (functionDeclarations.length > 0) {\n setupMessage.setup.tools = [{ function_declarations: functionDeclarations }];\n this.log('Including tools in setup message', { toolCount: functionDeclarations.length });\n }\n\n this.log('Sending Live API setup message:', setupMessage);\n\n try {\n this.sendEvent('setup', setupMessage);\n } catch (error) {\n this.log('Failed to send Live API setup message:', error);\n throw new Error(\n `Failed to send Live API setup message: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Wait for Gemini Live session to be created and ready\n * @private\n */\n private waitForSessionCreated(): Promise<void> {\n return new Promise((resolve, reject) => {\n // For Gemini Live API, we need to wait for the setup completion\n // This will be triggered by the setupComplete message type\n\n let isResolved = false;\n\n const onSetupComplete = () => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n resolve();\n }\n };\n\n const onError = (errorData: { message?: string; code?: string; details?: unknown }) => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(new Error(`Session creation failed: ${errorData.message || 'Unknown error'}`));\n }\n };\n\n const onSessionEnd = () => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(new Error('Session ended before setup completed'));\n }\n };\n\n const cleanup = () => {\n this.eventManager.getEventEmitter().removeListener('setupComplete', onSetupComplete as any);\n this.eventManager.getEventEmitter().removeListener('error', onError as any);\n this.eventManager.getEventEmitter().removeListener('sessionEnd', onSessionEnd as any);\n };\n\n // Listen for setup completion\n this.eventManager.getEventEmitter().once('setupComplete', onSetupComplete as any);\n this.eventManager.getEventEmitter().once('error', onError as any);\n this.eventManager.getEventEmitter().once('sessionEnd', onSessionEnd as any);\n\n // Add timeout to prevent hanging indefinitely\n setTimeout(() => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(new Error('Session creation timeout'));\n }\n }, 30000); // 30 second timeout\n });\n }\n\n /**\n * Get OAuth access token for Vertex AI authentication\n * Implements token caching and automatic refresh\n * @private\n */\n private async getAccessToken(): Promise<string> {\n if (!this.options.vertexAI) {\n throw new Error('getAccessToken should only be called for Vertex AI mode');\n }\n return this.authManager.getAccessToken();\n }\n\n /**\n * Get the current response ID from the server message\n * This is needed to associate audio chunks with their respective responses.\n * @private\n */\n private getCurrentResponseId(): string | undefined {\n return this.audioStreamManager.getCurrentResponseId();\n }\n\n /**\n * Set the current response ID for the next audio chunk.\n * This is used to track the response ID for the current turn.\n * @private\n */\n private setCurrentResponseId(responseId: string): void {\n this.audioStreamManager.setCurrentResponseId(responseId);\n }\n\n /**\n * Send an event to the Gemini Live API with queueing support\n * @private\n */\n private sendEvent(type: string, data: any): void {\n // Handle messages that already have their own structure\n let message: any;\n if (type === 'setup' && data.setup) {\n // For setup messages, use the data as-is\n message = data;\n } else if (type === 'client_content' && data.client_content) {\n // For client_content messages, use the data as-is\n message = data;\n } else if (type === 'realtime_input' && data.realtime_input) {\n // For realtime_input messages, use the data as-is\n message = data;\n } else if (type === 'toolResponse' && data.toolResponse) {\n // For toolResponse messages, use the data as-is\n message = data;\n } else if (type === 'session.update' && data.session) {\n // For session update messages, use the data as-is\n message = data;\n } else {\n // For other messages, create the standard structure\n message = { type: type, ...data };\n }\n\n if (!this.ws || !this.connectionManager.isConnected()) {\n // Queue the message if WebSocket is not ready\n this.queue.push(message);\n this.log('Queued message:', { type, data });\n } else {\n // Send immediately if WebSocket is ready\n this.connectionManager.send(JSON.stringify(message));\n this.log('Sent message:', { type, data });\n }\n }\n\n /**\n * Equip the voice provider with tools\n * @param tools Object containing tool definitions that can be called by the voice model\n *\n * @example\n * ```typescript\n * const weatherTool = createTool({\n * id: \"getWeather\",\n * description: \"Get the current weather for a location\",\n * inputSchema: z.object({\n * location: z.string().describe(\"The city and state, e.g. San Francisco, CA\"),\n * }),\n * execute: async (inputData) => {\n * // Fetch weather data from an API\n * const response = await fetch(\n * `https://api.weather.com?location=${encodeURIComponent(inputData.location)}`,\n * );\n * const data = await response.json();\n * return {\n * message: `The current temperature in ${inputData.location} is ${data.temperature}°F with ${data.conditions}.`,\n * };\n * },\n * });\n *\n * voice.addTools({\n * getWeather: weatherTool,\n * });\n * ```\n */\n addTools(tools: ToolsInput): void {\n this.tools = tools;\n this.log('Tools added to Gemini Live Voice', { toolCount: Object.keys(tools || {}).length });\n }\n\n /**\n * Get the current tools configured for this voice instance\n * @returns Object containing the current tools\n */\n listTools(): ToolsInput | undefined {\n return this.tools;\n }\n\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[GeminiLiveVoice] ${message}`, ...args);\n }\n }\n\n /**\n * Flatten both tool sources (constructor `tools` option and runtime `addTools()` registry) into\n * the single declaration array Gemini Live expects inside `tools[0].function_declarations`.\n * Used by both `sendInitialConfig()` and `updateSessionConfig()` so mid-session tool updates\n * stay on the same shape as setup-time tools.\n * @private\n */\n private buildToolDeclarations(\n configTools: GeminiToolConfig[] | undefined,\n registeredTools: ToolsInput | undefined,\n ): Array<{ name: string; description?: string; parameters?: unknown }> {\n const declarations: Array<{ name: string; description?: string; parameters?: unknown }> = [];\n\n if (configTools && configTools.length > 0) {\n for (const tool of configTools) {\n declarations.push({\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n });\n }\n }\n\n if (registeredTools && Object.keys(registeredTools).length > 0) {\n for (const [toolName, tool] of Object.entries(registeredTools)) {\n try {\n let parameters: unknown;\n if ('inputSchema' in tool && tool.inputSchema) {\n if (typeof tool.inputSchema === 'object' && 'safeParse' in tool.inputSchema) {\n parameters = this.convertZodSchemaToJsonSchema(tool.inputSchema);\n } else {\n parameters = tool.inputSchema;\n }\n } else if ('parameters' in tool && tool.parameters) {\n parameters = tool.parameters;\n } else {\n parameters = { type: 'object', properties: {} };\n }\n\n declarations.push({\n name: toolName,\n description: tool.description || `Tool: ${toolName}`,\n parameters,\n });\n } catch (error) {\n this.log('Failed to process tool', { toolName, error });\n }\n }\n }\n\n return declarations;\n }\n\n /**\n * Convert a schema to Gemini-compatible JSON Schema using GoogleSchemaCompatLayer.\n * This ensures the output conforms to the OpenAPI 3.0 Schema Object subset\n * that Gemini's wire validator expects.\n * @private\n */\n private convertZodSchemaToJsonSchema(schema: unknown): unknown {\n try {\n const compat = new GoogleSchemaCompatLayer({\n provider: 'google',\n modelId: 'gemini-live',\n supportsStructuredOutputs: false,\n });\n return compat.processToJSONSchema(schema as any);\n } catch (error) {\n this.log('Failed to convert Zod schema to JSON schema', { error, schema });\n return {\n type: 'object',\n properties: {},\n description: 'Schema conversion failed',\n };\n }\n }\n\n /**\n * Close the connection (alias for disconnect)\n */\n close(): void {\n void this.disconnect();\n }\n\n /**\n * Trigger voice provider to respond\n */\n async answer(_options?: Record<string, unknown>): Promise<void> {\n this.validateConnectionState();\n\n // Send a signal to trigger response generation\n this.sendEvent('response.create', {});\n }\n\n /**\n * Equip the voice provider with instructions\n * @param instructions Instructions to add\n */\n addInstructions(instructions?: string): void {\n if (instructions) {\n this.options.instructions = instructions;\n this.log('Instructions added:', instructions);\n }\n }\n}\n\nexport type { IncrementalTurn } from './types';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#adapterContext","#correlate","#export","#rawConfig"],"sources":["../../../packages/_internal-core/dist/logger/index.js","../../../packages/_internal-core/dist/base/index.js","../../../packages/_internals/voice/dist/aisdk-C_K-VCwq.js","../src/managers/AudioStreamManager.ts","../src/utils/errors.ts","../src/managers/ConnectionManager.ts","../src/managers/ContextManager.ts","../src/managers/AuthManager.ts","../src/managers/EventManager.ts","../src/index.ts"],"sourcesContent":["import { Transform } from \"stream\";\n//#region src/logger/adapter.ts\nfunction isAdaptableLogger(logger) {\n\treturn typeof logger.__attachObservability === \"function\";\n}\n/**\n* Export a tracked exception through the adapter sink, mirroring the\n* DualLogger dual-write shape (`errorId`/`domain`/`category`/`details`/`cause`\n* when present on a MastraError-like value). Never throws into the caller.\n*/\nfunction exportTrackedException(ctx, error, metadata) {\n\tif (!ctx?.options.export) return;\n\ttry {\n\t\tconst mastraError = error;\n\t\tctx.getLogSink()?.error(error.message, {\n\t\t\t...mastraError.id !== void 0 ? { errorId: mastraError.id } : {},\n\t\t\t...mastraError.domain !== void 0 ? { domain: mastraError.domain } : {},\n\t\t\t...mastraError.category !== void 0 ? { category: mastraError.category } : {},\n\t\t\t...mastraError.details !== void 0 ? { details: mastraError.details } : {},\n\t\t\t...error.cause instanceof Error ? { cause: error.cause.message } : {},\n\t\t\t...metadata\n\t\t});\n\t} catch {}\n}\n/**\n* Adapt IMastraLogger's variadic args into the structured `data` payload of\n* an exported log record. Extracts the first plain object as data,\n* serializes an Error arg, and collects remaining primitives under `args`\n* so the derived record preserves all context from the native call.\n*/\nfunction buildLogRecordData(args) {\n\tconst objectData = args.find((arg) => arg !== null && typeof arg === \"object\" && !Array.isArray(arg) && !(arg instanceof Error));\n\tconst errorArg = args.find((arg) => arg instanceof Error);\n\tconst extraArgs = args.filter((arg) => arg !== objectData && arg !== errorArg);\n\tif (!objectData && !errorArg && extraArgs.length === 0) return void 0;\n\treturn {\n\t\t...objectData ?? {},\n\t\t...errorArg ? { error: {\n\t\t\tname: errorArg.name,\n\t\t\tmessage: errorArg.message,\n\t\t\tstack: errorArg.stack\n\t\t} } : {},\n\t\t...extraArgs.length > 0 ? { args: extraArgs } : {}\n\t};\n}\n//#endregion\n//#region src/logger/index.ts\nconst RegisteredLogger = {\n\tAGENT: \"AGENT\",\n\tOBSERVABILITY: \"OBSERVABILITY\",\n\tAUTH: \"AUTH\",\n\tBROWSER: \"BROWSER\",\n\tNETWORK: \"NETWORK\",\n\tWORKFLOW: \"WORKFLOW\",\n\tLLM: \"LLM\",\n\tTTS: \"TTS\",\n\tVOICE: \"VOICE\",\n\tVECTOR: \"VECTOR\",\n\tBUNDLER: \"BUNDLER\",\n\tDEPLOYER: \"DEPLOYER\",\n\tMEMORY: \"MEMORY\",\n\tSTORAGE: \"STORAGE\",\n\tEMBEDDINGS: \"EMBEDDINGS\",\n\tMCP_SERVER: \"MCP_SERVER\",\n\tSERVER_CACHE: \"SERVER_CACHE\",\n\tSERVER: \"SERVER\",\n\tWORKSPACE: \"WORKSPACE\",\n\tCHANNEL: \"CHANNEL\"\n};\nconst LogLevel = {\n\tDEBUG: \"debug\",\n\tINFO: \"info\",\n\tWARN: \"warn\",\n\tERROR: \"error\",\n\tNONE: \"silent\"\n};\nvar LoggerTransport = class extends Transform {\n\tconstructor(opts = {}) {\n\t\tsuper({\n\t\t\t...opts,\n\t\t\tobjectMode: true\n\t\t});\n\t}\n\tasync listLogsByRunId(_args) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _args?.page ?? 1,\n\t\t\tperPage: _args?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n\tasync listLogs(_args) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _args?.page ?? 1,\n\t\t\tperPage: _args?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n};\nconst createCustomTransport = (stream, listLogs, listLogsByRunId) => {\n\tlet transport = stream;\n\tif (listLogs) transport.listLogs = listLogs;\n\tif (listLogsByRunId) transport.listLogsByRunId = listLogsByRunId;\n\treturn transport;\n};\nvar MastraLogger = class {\n\tname;\n\tlevel;\n\ttransports;\n\tconstructor(options = {}) {\n\t\tthis.name = options.name || \"Mastra\";\n\t\tthis.level = options.level || LogLevel.ERROR;\n\t\tthis.transports = new Map(Object.entries(options.transports || {}));\n\t}\n\tgetTransports() {\n\t\treturn this.transports;\n\t}\n\ttrackException(_error, _metadata) {}\n\tasync listLogs(transportId, params) {\n\t\tif (!transportId || !this.transports.has(transportId)) return {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: params?.page ?? 1,\n\t\t\tperPage: params?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t\treturn this.transports.get(transportId).listLogs?.(params) ?? {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: params?.page ?? 1,\n\t\t\tperPage: params?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n\tasync listLogsByRunId({ transportId, runId, fromDate, toDate, logLevel, filters, page, perPage }) {\n\t\tif (!transportId || !this.transports.has(transportId) || !runId) return {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: page ?? 1,\n\t\t\tperPage: perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t\treturn this.transports.get(transportId).listLogsByRunId?.({\n\t\t\trunId,\n\t\t\tfromDate,\n\t\t\ttoDate,\n\t\t\tlogLevel,\n\t\t\tfilters,\n\t\t\tpage,\n\t\t\tperPage\n\t\t}) ?? {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: page ?? 1,\n\t\t\tperPage: perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n};\nvar ConsoleLogger = class ConsoleLogger extends MastraLogger {\n\tcomponent;\n\tfilter;\n\t#adapterContext;\n\tconstructor(options = {}) {\n\t\tsuper(options);\n\t\tthis.component = options.component;\n\t\tthis.filter = options.filter;\n\t}\n\t/**\n\t* Adapter hook (see `AdaptableLogger`): enables native trace correlation\n\t* (trace_id/span_id appended to console output) and observability export\n\t* derived from the same record. Called by Mastra during setup.\n\t*/\n\t__attachObservability(ctx) {\n\t\tthis.#adapterContext = ctx;\n\t}\n\tchild(componentOrBindings) {\n\t\tconst component = typeof componentOrBindings === \"string\" ? componentOrBindings : componentOrBindings?.component ?? this.component;\n\t\tconst child = new ConsoleLogger({\n\t\t\tname: this.name,\n\t\t\tlevel: this.level,\n\t\t\tcomponent,\n\t\t\tfilter: this.filter\n\t\t});\n\t\tif (this.#adapterContext) child.__attachObservability(this.#adapterContext);\n\t\treturn child;\n\t}\n\t/**\n\t* Native-record correlation: when a span is active, append the trace\n\t* fields object to the console args so every destination sees them.\n\t*/\n\t#correlate(args) {\n\t\tconst ctx = this.#adapterContext;\n\t\tif (!ctx?.options.correlation) return args;\n\t\ttry {\n\t\t\tconst fields = ctx.resolveTraceFields();\n\t\t\treturn fields ? [...args, fields] : args;\n\t\t} catch {\n\t\t\treturn args;\n\t\t}\n\t}\n\t/**\n\t* Export the record derived from the same native call to observability.\n\t* Mirrors DualLogger semantics: export is independent of the console\n\t* level filter, and never throws into the caller.\n\t*/\n\t#export(level, message, args) {\n\t\tconst ctx = this.#adapterContext;\n\t\tif (!ctx?.options.export) return;\n\t\ttry {\n\t\t\tctx.getLogSink()?.[level](message, buildLogRecordData(args));\n\t\t} catch {}\n\t}\n\tshouldLog(level, message, args) {\n\t\tif (!this.filter) return true;\n\t\ttry {\n\t\t\treturn this.filter({\n\t\t\t\tcomponent: this.component,\n\t\t\t\tlevel,\n\t\t\t\tmessage,\n\t\t\t\targs\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tconsole.error(`[Logger] Filter error for component=${this.component} level=${level}:`, e);\n\t\t\treturn true;\n\t\t}\n\t}\n\tprefix() {\n\t\treturn this.component ? `[${this.component}] ` : \"\";\n\t}\n\tdebug(message, ...args) {\n\t\tif (this.level === LogLevel.DEBUG && this.shouldLog(LogLevel.DEBUG, message, args)) console.info(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"debug\", message, args);\n\t}\n\tinfo(message, ...args) {\n\t\tif ((this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.INFO, message, args)) console.info(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"info\", message, args);\n\t}\n\twarn(message, ...args) {\n\t\tif ((this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.WARN, message, args)) console.warn(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"warn\", message, args);\n\t}\n\terror(message, ...args) {\n\t\tif ((this.level === LogLevel.ERROR || this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.ERROR, message, args)) console.error(`${this.prefix()}${message}`, ...this.#correlate(args));\n\t\tthis.#export(\"error\", message, args);\n\t}\n\ttrackException(error, metadata) {\n\t\texportTrackedException(this.#adapterContext, error, metadata);\n\t}\n\tasync listLogs(_transportId, _params) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _params?.page ?? 1,\n\t\t\tperPage: _params?.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n\tasync listLogsByRunId(_args) {\n\t\treturn {\n\t\t\tlogs: [],\n\t\t\ttotal: 0,\n\t\t\tpage: _args.page ?? 1,\n\t\t\tperPage: _args.perPage ?? 100,\n\t\t\thasMore: false\n\t\t};\n\t}\n};\n//#endregion\nexport { ConsoleLogger, LogLevel, LoggerTransport, MastraLogger, RegisteredLogger, buildLogRecordData, createCustomTransport, exportTrackedException, isAdaptableLogger };\n\n//# sourceMappingURL=index.js.map","import { ConsoleLogger, RegisteredLogger } from \"../logger/index.js\";\n//#region src/base/MastraBase.ts\nvar MastraBase = class {\n\tcomponent = RegisteredLogger.LLM;\n\tlogger;\n\tname;\n\t#rawConfig;\n\tconstructor({ component, name, rawConfig }) {\n\t\tthis.component = component || RegisteredLogger.LLM;\n\t\tthis.name = name;\n\t\tthis.#rawConfig = rawConfig;\n\t\tthis.logger = new ConsoleLogger({ name: `${this.component} - ${this.name}` });\n\t}\n\t/**\n\t* Returns the raw storage configuration this primitive was created from,\n\t* or undefined if it was created from code.\n\t*/\n\ttoRawConfig() {\n\t\treturn this.#rawConfig;\n\t}\n\t/**\n\t* Sets the raw storage configuration for this primitive.\n\t* @internal\n\t*/\n\t__setRawConfig(rawConfig) {\n\t\tthis.#rawConfig = rawConfig;\n\t}\n\t/**\n\t* Set the logger for the agent\n\t* @param logger\n\t*/\n\t__setLogger(logger) {\n\t\tthis.logger = \"child\" in logger && typeof logger.child === \"function\" ? logger.child({ component: this.component }) : logger;\n\t}\n};\n//#endregion\nexport { MastraBase };\n\n//# sourceMappingURL=index.js.map","import { MastraBase } from \"@internal/core/base\";\nimport { PassThrough } from \"stream\";\nimport { experimental_generateSpeech, experimental_transcribe } from \"@internal/ai-sdk-v5\";\n//#region src/voice/voice.ts\nvar MastraVoice = class extends MastraBase {\n\tlisteningModel;\n\tspeechModel;\n\tspeaker;\n\trealtimeConfig;\n\tconstructor({ listeningModel, speechModel, speaker, realtimeConfig, name } = {}) {\n\t\tsuper({\n\t\t\tcomponent: \"VOICE\",\n\t\t\tname\n\t\t});\n\t\tthis.listeningModel = listeningModel;\n\t\tthis.speechModel = speechModel;\n\t\tthis.speaker = speaker;\n\t\tthis.realtimeConfig = realtimeConfig;\n\t}\n\t/**\n\t* Custom serialization for tracing/observability spans.\n\t* Excludes `apiKey` from listeningModel / speechModel / realtimeConfig\n\t* and any provider-specific state held by subclasses. Subclasses that\n\t* need to expose additional non-sensitive fields can override.\n\t*/\n\tserializeForSpan() {\n\t\treturn {\n\t\t\tcomponent: \"VOICE\",\n\t\t\tname: this.name,\n\t\t\tspeaker: this.speaker,\n\t\t\tlisteningModel: this.listeningModel ? { name: this.listeningModel.name } : void 0,\n\t\t\tspeechModel: this.speechModel ? { name: this.speechModel.name } : void 0,\n\t\t\trealtimeModel: this.realtimeConfig?.model\n\t\t};\n\t}\n\tupdateConfig(_options) {\n\t\tthis.logger.debug(\"updateConfig not implemented by this voice provider\");\n\t}\n\t/**\n\t* Initializes a WebSocket or WebRTC connection for real-time communication\n\t* @returns Promise that resolves when the connection is established\n\t*/\n\tasync connect(_options) {\n\t\tthis.logger.debug(\"connect not implemented by this voice provider\");\n\t}\n\t/**\n\t* Relay audio data to the voice provider for real-time processing\n\t* @param audioData Audio data to relay\n\t*/\n\tasync send(_audioData) {\n\t\tthis.logger.debug(\"relay not implemented by this voice provider\");\n\t}\n\t/**\n\t* Trigger voice providers to respond\n\t*/\n\tasync answer(_options) {\n\t\tthis.logger.debug(\"answer not implemented by this voice provider\");\n\t}\n\t/**\n\t* Equip the voice provider with instructions\n\t* @param instructions Instructions to add\n\t*/\n\taddInstructions(_instructions) {}\n\t/**\n\t* Equip the voice provider with tools\n\t* @param tools Array of tools to add\n\t*/\n\taddTools(_tools) {}\n\t/**\n\t* Disconnect from the WebSocket or WebRTC connection\n\t*/\n\tclose() {\n\t\tthis.logger.debug(\"close not implemented by this voice provider\");\n\t}\n\t/**\n\t* Register an event listener\n\t* @param event Event name (e.g., 'speaking', 'writing', 'error')\n\t* @param callback Callback function that receives event data\n\t*/\n\ton(_event, _callback) {\n\t\tthis.logger.debug(\"on not implemented by this voice provider\");\n\t}\n\t/**\n\t* Remove an event listener\n\t* @param event Event name (e.g., 'speaking', 'writing', 'error')\n\t* @param callback Callback function to remove\n\t*/\n\toff(_event, _callback) {\n\t\tthis.logger.debug(\"off not implemented by this voice provider\");\n\t}\n\t/**\n\t* Get available speakers/voices\n\t* @returns Array of available voice IDs and their metadata\n\t*/\n\tgetSpeakers() {\n\t\tthis.logger.debug(\"getSpeakers not implemented by this voice provider\");\n\t\treturn Promise.resolve([]);\n\t}\n\t/**\n\t* Get available speakers/voices\n\t* @returns Array of available voice IDs and their metadata\n\t*/\n\tgetListener() {\n\t\tthis.logger.debug(\"getListener not implemented by this voice provider\");\n\t\treturn Promise.resolve({ enabled: false });\n\t}\n};\n//#endregion\n//#region src/voice/aisdk/speech.ts\nvar AISDKSpeech = class extends MastraVoice {\n\tmodel;\n\tdefaultVoice;\n\tconstructor(model, options) {\n\t\tsuper({ name: \"ai-sdk-speech\" });\n\t\tthis.model = model;\n\t\tthis.defaultVoice = options?.voice;\n\t}\n\tasync speak(input, options) {\n\t\tconst text = typeof input === \"string\" ? input : await this.streamToText(input);\n\t\tconst result = await experimental_generateSpeech({\n\t\t\tmodel: this.model,\n\t\t\ttext,\n\t\t\tvoice: options?.speaker || this.defaultVoice,\n\t\t\tlanguage: options?.language,\n\t\t\tproviderOptions: options?.providerOptions,\n\t\t\tabortSignal: options?.abortSignal,\n\t\t\theaders: options?.headers\n\t\t});\n\t\tconst stream = new PassThrough();\n\t\tstream.end(Buffer.from(result.audio.uint8Array));\n\t\treturn stream;\n\t}\n\tasync listen() {\n\t\tthrow new Error(\"AI SDK speech models do not support transcription. Use AISDKTranscription instead.\");\n\t}\n\tasync getSpeakers() {\n\t\treturn [];\n\t}\n\tasync getListener() {\n\t\treturn { enabled: false };\n\t}\n\tasync streamToText(stream) {\n\t\tconst chunks = [];\n\t\tfor await (const chunk of stream) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\treturn Buffer.concat(chunks).toString(\"utf-8\");\n\t}\n};\n//#endregion\n//#region src/voice/aisdk/transcription.ts\nvar AISDKTranscription = class extends MastraVoice {\n\tmodel;\n\tconstructor(model) {\n\t\tsuper({ name: \"ai-sdk-transcription\" });\n\t\tthis.model = model;\n\t}\n\tasync speak() {\n\t\tthrow new Error(\"AI SDK transcription models do not support text-to-speech. Use AISDKSpeech instead.\");\n\t}\n\tasync getSpeakers() {\n\t\treturn [];\n\t}\n\tasync getListener() {\n\t\treturn { enabled: true };\n\t}\n\t/**\n\t* Transcribe audio to text\n\t* For enhanced metadata (segments, language, duration), use AI SDK's transcribe() directly\n\t*/\n\tasync listen(audioStream, options) {\n\t\tconst audioBuffer = await this.convertToBuffer(audioStream);\n\t\treturn (await experimental_transcribe({\n\t\t\tmodel: this.model,\n\t\t\taudio: audioBuffer,\n\t\t\tproviderOptions: options?.providerOptions,\n\t\t\tabortSignal: options?.abortSignal,\n\t\t\theaders: options?.headers\n\t\t})).text;\n\t}\n\tasync convertToBuffer(audio) {\n\t\tif (Buffer.isBuffer(audio)) return audio;\n\t\tif (audio instanceof Uint8Array) return Buffer.from(audio);\n\t\tif (typeof audio === \"string\") return Buffer.from(audio, \"base64\");\n\t\tconst chunks = [];\n\t\tfor await (const chunk of audio) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n\t\treturn Buffer.concat(chunks);\n\t}\n};\n//#endregion\nexport { AISDKSpeech as n, MastraVoice as r, AISDKTranscription as t };\n\n//# sourceMappingURL=aisdk-C_K-VCwq.js.map","import { PassThrough } from 'node:stream';\nimport type { AudioConfig } from '../types';\n\n/**\n * Default audio configuration for Gemini Live API\n */\nexport const DEFAULT_AUDIO_CONFIG: AudioConfig = {\n inputSampleRate: 16000,\n outputSampleRate: 24000,\n encoding: 'pcm16',\n channels: 1,\n};\n\n/**\n * Manages audio streams for the Gemini Live Voice API\n * Handles speaker stream lifecycle, cleanup, and audio processing\n */\nexport class AudioStreamManager {\n private speakerStreams = new Map<string, PassThrough & { id?: string; created?: number }>();\n private currentResponseId?: string;\n private readonly MAX_CONCURRENT_STREAMS = 10;\n private readonly STREAM_TIMEOUT_MS = 30000; // 30 seconds\n private readonly debug: boolean;\n private readonly audioConfig: AudioConfig;\n private readonly maxChunkSize = 32768; // 32KB max chunk size per Gemini limits\n private readonly minSendInterval = 0; // No throttling - let the stream control the pace\n private lastSendTime = 0;\n private pendingChunks: Array<{ chunk: Buffer; timestamp: number }> = [];\n private pendingTimer?: NodeJS.Timeout;\n private sendToGemini?: (type: 'realtime_input' | 'client_content', message: Record<string, unknown>) => void;\n\n // Audio buffer management constants\n private readonly MAX_BUFFER_SIZE = 50 * 1024 * 1024; // 50MB maximum buffer size\n private readonly MAX_AUDIO_DURATION = 300; // 5 minutes maximum audio duration\n\n constructor(audioConfig: AudioConfig, debug: boolean = false) {\n this.audioConfig = audioConfig;\n this.debug = debug;\n }\n\n /**\n * Provide a sender callback that will be used to deliver messages to Gemini\n */\n setSender(sender: (type: 'realtime_input' | 'client_content', message: Record<string, unknown>) => void): void {\n this.sendToGemini = sender;\n }\n\n /**\n * Get the default audio configuration\n */\n static getDefaultAudioConfig(): AudioConfig {\n return { ...DEFAULT_AUDIO_CONFIG };\n }\n\n /**\n * Create a merged audio configuration with defaults\n */\n static createAudioConfig(customConfig?: Partial<AudioConfig>): AudioConfig {\n return {\n ...DEFAULT_AUDIO_CONFIG,\n ...customConfig,\n };\n }\n\n /**\n * Get the current response ID for the next audio chunk\n */\n getCurrentResponseId(): string | undefined {\n return this.currentResponseId;\n }\n\n /**\n * Set the current response ID for the next audio chunk\n */\n setCurrentResponseId(responseId: string): void {\n this.currentResponseId = responseId;\n }\n\n /**\n * Get the current speaker stream\n */\n getCurrentSpeakerStream(): NodeJS.ReadableStream | null {\n const currentResponseId = this.getCurrentResponseId();\n if (!currentResponseId) {\n return null;\n }\n\n const currentStream = this.speakerStreams.get(currentResponseId);\n return currentStream ? (currentStream as NodeJS.ReadableStream) : null;\n }\n\n /**\n * Add a new speaker stream for a response\n */\n addSpeakerStream(responseId: string, stream: PassThrough): void {\n const streamWithMetadata = Object.assign(stream, {\n id: responseId,\n created: Date.now(),\n });\n\n this.speakerStreams.set(responseId, streamWithMetadata);\n this.log(`Added speaker stream for response: ${responseId}`);\n\n // Enforce stream limits after adding\n this.enforceStreamLimits();\n }\n\n /**\n * Remove a specific speaker stream\n */\n removeSpeakerStream(responseId: string): void {\n const stream = this.speakerStreams.get(responseId);\n if (stream && !stream.destroyed) {\n stream.end();\n setTimeout(() => {\n if (!stream.destroyed) {\n stream.destroy();\n this.log(`Force destroyed stream for response: ${responseId}`);\n }\n }, 1000);\n }\n\n this.speakerStreams.delete(responseId);\n this.log(`Removed speaker stream for response: ${responseId}`);\n }\n\n /**\n * Clean up all speaker streams\n */\n cleanupSpeakerStreams(): void {\n try {\n if (this.speakerStreams.size === 0) {\n return;\n }\n\n this.log(`Cleaning up ${this.speakerStreams.size} speaker streams`);\n\n for (const [responseId, stream] of this.speakerStreams.entries()) {\n try {\n // Check if stream is already ended/destroyed\n if (!stream.destroyed) {\n stream.end();\n\n // Force destroy after a short timeout if end() doesn't work\n setTimeout(() => {\n if (!stream.destroyed) {\n stream.destroy();\n this.log(`Force destroyed stream for response: ${responseId}`);\n }\n }, 1000);\n }\n\n this.speakerStreams.delete(responseId);\n this.log(`Cleaned up speaker stream for response: ${responseId}`);\n } catch (streamError) {\n this.log(`Error cleaning up stream ${responseId}:`, streamError);\n // Force remove from map even if cleanup failed\n this.speakerStreams.delete(responseId);\n }\n }\n\n this.currentResponseId = undefined;\n this.log('All speaker streams cleaned up');\n } catch (error) {\n this.log('Error during speaker stream cleanup:', error);\n // Force clear the map if cleanup fails\n this.speakerStreams.clear();\n this.currentResponseId = undefined;\n }\n }\n\n /**\n * Clean up old/stale streams to prevent memory leaks\n */\n cleanupStaleStreams(): void {\n try {\n const now = Date.now();\n const staleCutoff = now - this.STREAM_TIMEOUT_MS;\n const staleStreams: string[] = [];\n\n for (const [responseId, stream] of this.speakerStreams.entries()) {\n const created = stream.created || 0;\n if (created < staleCutoff) {\n staleStreams.push(responseId);\n }\n }\n\n if (staleStreams.length > 0) {\n this.log(`Cleaning up ${staleStreams.length} stale streams`);\n for (const responseId of staleStreams) {\n const stream = this.speakerStreams.get(responseId);\n if (stream && !stream.destroyed) {\n stream.end();\n }\n this.speakerStreams.delete(responseId);\n }\n }\n } catch (error) {\n this.log('Error cleaning up stale streams:', error);\n }\n }\n\n /**\n * Enforce stream limits to prevent memory exhaustion\n */\n enforceStreamLimits(): void {\n try {\n if (this.speakerStreams.size <= this.MAX_CONCURRENT_STREAMS) {\n return;\n }\n\n this.log(\n `Stream limit exceeded (${this.speakerStreams.size}/${this.MAX_CONCURRENT_STREAMS}), cleaning up oldest streams`,\n );\n\n // Sort streams by creation time and remove oldest ones\n const sortedStreams = Array.from(this.speakerStreams.entries()).sort(\n ([, a], [, b]) => (a.created || 0) - (b.created || 0),\n );\n\n const streamsToRemove = sortedStreams.slice(0, this.speakerStreams.size - this.MAX_CONCURRENT_STREAMS);\n\n for (const [responseId, stream] of streamsToRemove) {\n if (!stream.destroyed) {\n stream.end();\n }\n this.speakerStreams.delete(responseId);\n this.log(`Removed old stream for response: ${responseId}`);\n }\n } catch (error) {\n this.log('Error enforcing stream limits:', error);\n }\n }\n\n /**\n * Get information about current streams for debugging\n */\n getStreamInfo(): {\n totalStreams: number;\n currentResponseId?: string;\n streamDetails: Array<{ responseId: string; created: number; destroyed: boolean }>;\n } {\n const streamDetails = Array.from(this.speakerStreams.entries()).map(([responseId, stream]) => ({\n responseId,\n created: stream.created || 0,\n destroyed: stream.destroyed,\n }));\n\n return {\n totalStreams: this.speakerStreams.size,\n currentResponseId: this.currentResponseId,\n streamDetails,\n };\n }\n\n /**\n * Convert Int16Array audio data to base64 string for WebSocket transmission\n */\n int16ArrayToBase64(int16Array: Int16Array): string {\n const buffer = new ArrayBuffer(int16Array.length * 2);\n const view = new DataView(buffer);\n\n // Convert Int16Array to bytes with little-endian format\n for (let i = 0; i < int16Array.length; i++) {\n view.setInt16(i * 2, int16Array[i]!, true);\n }\n\n const nodeBuffer = Buffer.from(buffer);\n return nodeBuffer.toString('base64');\n }\n\n /**\n * Convert base64 string to Int16Array audio data\n */\n base64ToInt16Array(base64Audio: string): Int16Array {\n try {\n const buffer = Buffer.from(base64Audio, 'base64');\n\n // Convert Buffer to Int16Array\n if (buffer.length % 2 !== 0) {\n throw new Error('Invalid audio data: buffer length must be even for 16-bit audio');\n }\n\n return new Int16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2);\n } catch (error) {\n throw new Error(\n `Failed to decode base64 audio data: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Validate and convert audio data to the required format for Gemini Live API\n * Gemini Live expects 16kHz PCM16 for input\n */\n validateAndConvertAudioInput(audioData: Buffer | Int16Array): Int16Array {\n if (Buffer.isBuffer(audioData)) {\n // Convert Buffer to Int16Array\n if (audioData.length % 2 !== 0) {\n throw new Error('Audio buffer length must be even for 16-bit audio');\n }\n return new Int16Array(audioData.buffer, audioData.byteOffset, audioData.byteLength / 2);\n }\n\n if (audioData instanceof Int16Array) {\n return audioData;\n }\n\n throw new Error('Unsupported audio data format. Expected Buffer or Int16Array');\n }\n\n /**\n * Process audio chunk for streaming - handles format validation and conversion\n */\n processAudioChunk(chunk: Buffer | Uint8Array | Int16Array): string {\n let int16Array: Int16Array;\n\n if (chunk instanceof Int16Array) {\n int16Array = chunk;\n } else if (Buffer.isBuffer(chunk)) {\n if (chunk.length % 2 !== 0) {\n throw new Error('Audio chunk length must be even for 16-bit audio');\n }\n int16Array = new Int16Array(chunk.buffer, chunk.byteOffset, chunk.byteLength / 2);\n } else if (chunk instanceof Uint8Array) {\n if (chunk.length % 2 !== 0) {\n throw new Error('Audio chunk length must be even for 16-bit audio');\n }\n int16Array = new Int16Array(chunk.buffer, chunk.byteOffset, chunk.byteLength / 2);\n } else {\n throw new Error('Unsupported audio chunk format');\n }\n\n return this.int16ArrayToBase64(int16Array);\n }\n\n /**\n * Validate audio format and sample rate for Gemini Live API requirements\n */\n validateAudioFormat(sampleRate?: number, channels?: number): void {\n if (sampleRate && sampleRate !== this.audioConfig.inputSampleRate) {\n this.log(\n `Warning: Audio sample rate ${sampleRate}Hz does not match expected ${this.audioConfig.inputSampleRate}Hz`,\n );\n }\n\n if (channels && channels !== this.audioConfig.channels) {\n throw new Error(`Unsupported channel count: ${channels}. Gemini Live API requires mono audio (1 channel)`);\n }\n }\n\n /**\n * Create an audio message for the Gemini Live API\n */\n createAudioMessage(audioData: string, messageType: 'input' | 'realtime' = 'realtime'): Record<string, unknown> {\n if (messageType === 'input') {\n // For conversation item creation (traditional listen method)\n return {\n client_content: {\n turns: [\n {\n role: 'user',\n parts: [\n {\n inlineData: {\n mimeType: 'audio/pcm',\n data: audioData,\n },\n },\n ],\n },\n ],\n turnComplete: true,\n },\n };\n } else {\n // For real-time streaming\n return {\n realtime_input: {\n audio: {\n mime_type: `audio/pcm;rate=${this.audioConfig.inputSampleRate ?? DEFAULT_AUDIO_CONFIG.inputSampleRate}`,\n data: audioData,\n },\n },\n };\n }\n }\n\n /**\n * Get a speaker stream by response ID\n */\n getSpeakerStream(responseId: string): PassThrough | undefined {\n return this.speakerStreams.get(responseId);\n }\n\n /**\n * Create a new speaker stream for a response ID\n */\n createSpeakerStream(responseId: string): PassThrough {\n const stream = new PassThrough() as PassThrough & { id?: string; created?: number };\n stream.id = responseId;\n stream.created = Date.now();\n\n // Add the stream to the manager\n this.addSpeakerStream(responseId, stream);\n\n return stream;\n }\n\n /**\n * Get the number of active streams\n */\n getActiveStreamCount(): number {\n return this.speakerStreams.size;\n }\n\n /**\n * Check if a specific response ID has an active stream\n */\n hasStream(responseId: string): boolean {\n return this.speakerStreams.has(responseId);\n }\n\n /**\n * Get all active response IDs\n */\n getActiveResponseIds(): string[] {\n return Array.from(this.speakerStreams.keys());\n }\n\n /**\n * Reset the manager state (useful for testing or reconnection)\n */\n reset(): void {\n this.cleanupSpeakerStreams();\n this.currentResponseId = undefined;\n this.log('AudioStreamManager reset');\n }\n\n /**\n * Validate audio chunk size and format\n */\n validateAudioChunk(chunk: Buffer): void {\n if (chunk.length === 0) {\n throw new Error('Audio chunk cannot be empty');\n }\n\n if (chunk.length > this.maxChunkSize) {\n throw new Error(`Audio chunk size ${chunk.length} exceeds maximum allowed size ${this.maxChunkSize}`);\n }\n\n if (chunk.length % 2 !== 0) {\n throw new Error('Audio chunk length must be even for 16-bit audio');\n }\n }\n\n /**\n * Send audio chunk with throttling and validation\n */\n sendAudioChunk(chunk: Buffer): void {\n try {\n this.validateAudioChunk(chunk);\n\n const now = Date.now();\n if (now - this.lastSendTime < this.minSendInterval) {\n // Throttle if needed - enqueue for later processing\n this.pendingChunks.push({ chunk, timestamp: now });\n const delay = this.minSendInterval - (now - this.lastSendTime);\n if (!this.pendingTimer) {\n this.pendingTimer = setTimeout(\n () => {\n this.pendingTimer = undefined;\n this.processPendingChunks();\n },\n Math.max(0, delay),\n );\n }\n return;\n }\n\n this.processChunk(chunk);\n this.processPendingChunks();\n } catch (error) {\n this.log('Error sending audio chunk:', error);\n throw error;\n }\n }\n\n /**\n * Handle audio stream processing\n */\n async handleAudioStream(stream: NodeJS.ReadableStream): Promise<void> {\n return new Promise((resolve, reject) => {\n const cleanup = () => {\n stream.removeAllListeners();\n };\n\n stream.on('data', (chunk: Buffer) => {\n try {\n if (chunk.length > this.maxChunkSize) {\n // Split large chunks\n const chunks = this.splitAudioChunk(chunk);\n for (const subChunk of chunks) {\n this.validateAudioChunk(subChunk);\n this.sendAudioChunk(subChunk);\n }\n } else {\n this.validateAudioChunk(chunk);\n this.sendAudioChunk(chunk);\n }\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n\n stream.on('end', () => {\n cleanup();\n resolve();\n });\n\n stream.on('error', error => {\n cleanup();\n reject(error);\n });\n });\n }\n\n /**\n * Split large audio chunks into smaller ones\n */\n private splitAudioChunk(chunk: Buffer): Buffer[] {\n const chunks: Buffer[] = [];\n let offset = 0;\n\n while (offset < chunk.length) {\n const size = Math.min(this.maxChunkSize, chunk.length - offset);\n chunks.push(chunk.subarray(offset, offset + size));\n offset += size;\n }\n\n return chunks;\n }\n\n /**\n * Calculate audio duration from buffer length\n */\n calculateAudioDuration(bufferLength: number, sampleRate?: number): number {\n const effectiveSampleRate = sampleRate || this.audioConfig.inputSampleRate;\n return bufferLength / (effectiveSampleRate * 2); // 2 bytes per sample for 16-bit audio\n }\n\n /**\n * Validate audio buffer size and duration\n */\n validateAudioBuffer(buffer: Buffer): void {\n if (buffer.length === 0) {\n throw new Error('Audio buffer cannot be empty');\n }\n\n if (buffer.length > this.MAX_BUFFER_SIZE) {\n throw new Error(\n `Audio buffer size ${buffer.length} exceeds maximum allowed size ${this.MAX_BUFFER_SIZE / (1024 * 1024)}MB`,\n );\n }\n\n if (buffer.length % 2 !== 0) {\n throw new Error('Audio buffer length must be even for 16-bit audio');\n }\n\n const duration = this.calculateAudioDuration(buffer.length);\n if (duration > this.MAX_AUDIO_DURATION) {\n throw new Error(\n `Audio duration ${duration.toFixed(2)}s exceeds maximum allowed duration ${this.MAX_AUDIO_DURATION}s`,\n );\n }\n }\n\n /**\n * Process audio buffer for transcription\n * Combines chunks, validates format, and converts to base64\n */\n processAudioBufferForTranscription(audioBuffer: Buffer): { base64Audio: string; duration: number; size: number } {\n // Validate audio format\n if (audioBuffer.length % 2 !== 0) {\n throw new Error('Invalid audio data: buffer length must be even for 16-bit audio');\n }\n\n // Calculate duration\n const duration = this.calculateAudioDuration(audioBuffer.length);\n\n // Convert to base64\n const base64Audio = audioBuffer.toString('base64');\n\n return {\n base64Audio,\n duration,\n size: audioBuffer.length,\n };\n }\n\n /**\n * Process audio chunks for transcription with buffer management\n * Handles chunk collection, size validation, and buffer management\n */\n processAudioChunksForTranscription(\n chunks: Buffer[],\n totalBufferSize: number,\n ): { audioBuffer: Buffer; base64Audio: string; duration: number; size: number } {\n // Check buffer size to prevent memory overflow\n if (totalBufferSize > this.MAX_BUFFER_SIZE) {\n throw new Error(`Audio data exceeds maximum size of ${this.MAX_BUFFER_SIZE / (1024 * 1024)}MB`);\n }\n\n // Combine all chunks\n const audioBuffer = Buffer.concat(chunks);\n\n // Process for transcription\n const result = this.processAudioBufferForTranscription(audioBuffer);\n\n return {\n audioBuffer,\n ...result,\n };\n }\n\n /**\n * Validate audio chunks and calculate total size\n */\n validateAudioChunks(chunks: Buffer[]): { totalSize: number; isValid: boolean; error?: string } {\n let totalSize = 0;\n\n for (const chunk of chunks) {\n if (!Buffer.isBuffer(chunk)) {\n return { totalSize: 0, isValid: false, error: 'Invalid chunk format' };\n }\n\n totalSize += chunk.length;\n\n if (totalSize > this.MAX_BUFFER_SIZE) {\n return {\n totalSize,\n isValid: false,\n error: `Total size ${totalSize} exceeds maximum allowed size ${this.MAX_BUFFER_SIZE}`,\n };\n }\n }\n\n return { totalSize, isValid: true };\n }\n\n /**\n * Get audio buffer limits and configuration\n */\n getAudioBufferLimits(): { maxBufferSize: number; maxAudioDuration: number; maxChunkSize: number } {\n return {\n maxBufferSize: this.MAX_BUFFER_SIZE,\n maxAudioDuration: this.MAX_AUDIO_DURATION,\n maxChunkSize: this.maxChunkSize,\n };\n }\n\n /**\n * Get audio configuration\n */\n getAudioConfig(): AudioConfig {\n return this.audioConfig;\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[AudioStreamManager] ${message}`, ...args);\n }\n }\n\n /**\n * Handle complete audio transcription workflow\n * Manages stream processing, chunk collection, and transcription\n */\n async handleAudioTranscription(\n audioStream: NodeJS.ReadableStream,\n sendAndAwaitTranscript: (base64Audio: string) => Promise<string>,\n onError: (error: Error) => void,\n timeoutMs: number = 30000,\n ): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let isCleanedUp = false;\n let totalBufferSize = 0;\n let isResolved = false;\n\n // Set up timeout\n const timeout = setTimeout(() => {\n if (!isResolved) {\n cleanup();\n reject(new Error(`Transcription timeout - no response received within ${timeoutMs / 1000} seconds`));\n }\n }, timeoutMs);\n\n // Stream event handlers\n const onStreamData = (chunk: Buffer) => {\n try {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\n // Check buffer size to prevent memory overflow\n totalBufferSize += buffer.length;\n if (totalBufferSize > this.MAX_BUFFER_SIZE) {\n cleanup();\n reject(new Error(`Audio data exceeds maximum size of ${this.MAX_BUFFER_SIZE / (1024 * 1024)}MB`));\n return;\n }\n\n chunks.push(buffer);\n } catch (error) {\n cleanup();\n reject(\n new Error(`Failed to process audio chunk: ${error instanceof Error ? error.message : 'Unknown error'}`),\n );\n }\n };\n\n const onStreamError = (error: Error) => {\n cleanup();\n reject(new Error(`Audio stream error: ${error.message}`));\n };\n\n const onStreamEnd = async () => {\n try {\n // Remove stream listeners as we're done with the stream\n audioStream.removeListener('data', onStreamData);\n audioStream.removeListener('error', onStreamError);\n\n // Process chunks for transcription\n const result = this.processAudioChunksForTranscription(chunks, totalBufferSize);\n\n this.log('Processing audio for transcription:', {\n chunks: chunks.length,\n totalSize: result.size,\n duration: result.duration,\n });\n\n // Send audio and await the transcript from the caller\n try {\n const transcript = await sendAndAwaitTranscript(result.base64Audio);\n if (!isResolved) {\n isResolved = true;\n cleanup();\n resolve(transcript.trim());\n }\n } catch (error) {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(\n new Error(\n `Failed to obtain transcription: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ),\n );\n }\n }\n } catch (error) {\n cleanup();\n reject(\n new Error(`Failed to process audio stream: ${error instanceof Error ? error.message : 'Unknown error'}`),\n );\n }\n };\n\n // Comprehensive cleanup function\n const cleanup = () => {\n if (isCleanedUp) return; // Prevent double cleanup\n isCleanedUp = true;\n\n // Clear all timers\n clearTimeout(timeout);\n\n // Remove stream event listeners\n audioStream.removeListener('data', onStreamData);\n audioStream.removeListener('error', onStreamError);\n audioStream.removeListener('end', onStreamEnd);\n\n // Clear chunks array to free memory\n chunks.length = 0;\n };\n\n // Set up stream event listeners\n audioStream.on('data', onStreamData);\n audioStream.on('error', onStreamError);\n audioStream.on('end', onStreamEnd);\n });\n }\n\n private processChunk(chunk: Buffer): void {\n // Convert raw audio buffer to base64 PCM16 and build message\n const base64Audio = this.processAudioChunk(chunk);\n const message = this.createAudioMessage(base64Audio, 'realtime');\n\n // Send via injected sender\n if (this.sendToGemini) {\n this.sendToGemini('realtime_input', message);\n } else {\n this.log('No sender configured for AudioStreamManager; dropping audio chunk');\n }\n\n // Update throttle timing and log\n this.lastSendTime = Date.now();\n this.log(`Sent audio chunk of size: ${chunk.length} bytes`);\n }\n\n private processPendingChunks(): void {\n while (this.pendingChunks.length > 0) {\n const nextChunk = this.pendingChunks[0];\n const now = Date.now();\n if (nextChunk && now - this.lastSendTime >= this.minSendInterval) {\n this.pendingChunks.shift();\n this.processChunk(nextChunk.chunk);\n } else {\n const delay = this.minSendInterval - (now - this.lastSendTime);\n if (!this.pendingTimer) {\n this.pendingTimer = setTimeout(\n () => {\n this.pendingTimer = undefined;\n this.processPendingChunks();\n },\n Math.max(0, delay),\n );\n }\n break;\n }\n }\n }\n}\n","import type { GeminiLiveErrorCode } from '../types';\n\n/**\n * Helper class for consistent error handling across managers and provider\n */\nexport class GeminiLiveError extends Error {\n public readonly code: string;\n public readonly details?: unknown;\n public readonly timestamp: number;\n\n constructor(code: GeminiLiveErrorCode | string, message: string, details?: unknown) {\n super(message);\n this.name = 'GeminiLiveError';\n this.code = code;\n this.details = details;\n this.timestamp = Date.now();\n }\n\n toEventData() {\n return {\n message: this.message,\n code: this.code,\n details: this.details,\n timestamp: this.timestamp,\n };\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { WebSocket } from 'ws';\nimport { GeminiLiveErrorCode } from '../types';\nimport { GeminiLiveError } from '../utils/errors';\n\nexport interface ConnectionConfig {\n debug: boolean;\n timeoutMs?: number;\n}\n\nexport class ConnectionManager {\n private ws?: WebSocket;\n private eventEmitter: EventEmitter;\n private readonly debug: boolean;\n private readonly timeoutMs: number;\n\n constructor(config: ConnectionConfig) {\n this.eventEmitter = new EventEmitter();\n this.debug = config.debug;\n this.timeoutMs = config.timeoutMs || 30000;\n }\n\n /**\n * Set the WebSocket instance\n */\n setWebSocket(ws: WebSocket): void {\n this.ws = ws;\n }\n\n /**\n * Get the current WebSocket instance\n */\n getWebSocket(): WebSocket | undefined {\n return this.ws;\n }\n\n /**\n * Check if WebSocket is connected\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Check if WebSocket is connecting\n */\n isConnecting(): boolean {\n return this.ws?.readyState === WebSocket.CONNECTING;\n }\n\n /**\n * Check if WebSocket is closed\n */\n isClosed(): boolean {\n return this.ws?.readyState === WebSocket.CLOSED;\n }\n\n /**\n * Wait for WebSocket to open\n */\n async waitForOpen(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.ws) {\n reject(new Error('WebSocket not initialized'));\n return;\n }\n\n // If already open, resolve immediately\n if (this.ws.readyState === WebSocket.OPEN) {\n resolve();\n return;\n }\n\n // Set up event listeners with cleanup\n const onOpen = () => {\n cleanup();\n resolve();\n };\n\n const onError = (error: Error) => {\n cleanup();\n reject(new Error(`WebSocket connection failed: ${error.message}`));\n };\n\n const onClose = () => {\n cleanup();\n reject(new Error('WebSocket connection closed before opening'));\n };\n\n const cleanup = () => {\n this.ws?.removeListener('open', onOpen);\n this.ws?.removeListener('error', onError);\n this.ws?.removeListener('close', onClose);\n };\n\n // Add event listeners\n this.ws.once('open', onOpen);\n this.ws.once('error', onError);\n this.ws.once('close', onClose);\n\n // Add timeout to prevent hanging indefinitely\n setTimeout(() => {\n cleanup();\n reject(new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_FAILED, 'WebSocket connection timeout'));\n }, this.timeoutMs);\n });\n }\n\n /**\n * Send data through WebSocket\n */\n send(data: string | Buffer): void {\n if (!this.ws) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket not initialized');\n }\n\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket is not open');\n }\n\n this.ws.send(data);\n }\n\n /**\n * Close the WebSocket connection\n */\n close(): void {\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n }\n\n /**\n * Get connection state\n */\n getConnectionState(): 'disconnected' | 'connecting' | 'connected' | 'closed' {\n if (!this.ws) return 'disconnected';\n\n switch (this.ws.readyState) {\n case WebSocket.CONNECTING:\n return 'connecting';\n case WebSocket.OPEN:\n return 'connected';\n case WebSocket.CLOSED:\n return 'closed';\n default:\n return 'disconnected';\n }\n }\n\n /**\n * Validate WebSocket state for operations\n */\n validateWebSocketState(): void {\n if (!this.ws) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket not initialized');\n }\n\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new GeminiLiveError(GeminiLiveErrorCode.CONNECTION_NOT_ESTABLISHED, 'WebSocket is not open');\n }\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[ConnectionManager] ${message}`, ...args);\n }\n }\n}\n","export interface ContextEntry {\n role: 'user' | 'assistant';\n content: string;\n timestamp: number;\n}\n\nexport interface ContextConfig {\n maxEntries?: number;\n maxContentLength?: number;\n compressionThreshold?: number;\n compressionEnabled?: boolean;\n}\n\nexport class ContextManager {\n private contextHistory: ContextEntry[] = [];\n private readonly maxEntries: number;\n private readonly maxContentLength: number;\n private readonly compressionThreshold: number;\n private compressionEnabled: boolean;\n\n constructor(config: ContextConfig = {}) {\n this.maxEntries = config.maxEntries || 100;\n this.maxContentLength = config.maxContentLength || 10000; // 10KB per entry\n this.compressionThreshold = config.compressionThreshold || 50;\n this.compressionEnabled = config.compressionEnabled ?? false;\n }\n\n /**\n * Add entry to context history\n */\n addEntry(role: 'user' | 'assistant', content: string): void {\n // Validate content length\n let processedContent = content;\n if (content.length > this.maxContentLength) {\n processedContent = content.substring(0, this.maxContentLength) + '...';\n }\n\n const entry: ContextEntry = {\n role,\n content: processedContent,\n timestamp: Date.now(),\n };\n\n this.contextHistory.push(entry);\n\n if (this.contextHistory.length > this.maxEntries) {\n if (this.compressionEnabled) {\n this.compressContext();\n } else {\n this.contextHistory = this.contextHistory.slice(-this.maxEntries);\n }\n }\n }\n\n /**\n * Get context history\n */\n getContextHistory(): ContextEntry[] {\n return [...this.contextHistory];\n }\n\n /**\n * Get context history as array of role/content pairs\n */\n getContextArray(): Array<{ role: string; content: string }> {\n return this.contextHistory.map(entry => ({\n role: entry.role,\n content: entry.content,\n }));\n }\n\n /**\n * Clear context history\n */\n clearContext(): void {\n this.contextHistory = [];\n }\n\n /**\n * Get context size\n */\n getContextSize(): number {\n return this.contextHistory.length;\n }\n\n /**\n * Compress context when it exceeds threshold\n */\n private compressContext(): void {\n if (!this.compressionEnabled || this.contextHistory.length <= this.compressionThreshold) {\n return;\n }\n\n // Keep first and last entries, compress middle ones\n const keepCount = Math.floor(this.compressionThreshold / 3);\n const firstEntries = this.contextHistory.slice(0, keepCount);\n const lastEntries = this.contextHistory.slice(-keepCount);\n\n // Create compressed middle entry\n const middleEntries = this.contextHistory.slice(keepCount, -keepCount);\n if (middleEntries.length > 0) {\n const compressedEntry: ContextEntry = {\n role: 'assistant',\n content: `[Compressed ${middleEntries.length} previous messages]`,\n timestamp: Date.now(),\n };\n\n this.contextHistory = [...firstEntries, compressedEntry, ...lastEntries];\n } else {\n this.contextHistory = [...firstEntries, ...lastEntries];\n }\n }\n\n /**\n * Enable or disable compression at runtime\n */\n setCompressionEnabled(enabled: boolean): void {\n this.compressionEnabled = enabled;\n }\n\n /**\n * Get context summary\n */\n getContextSummary(): {\n totalEntries: number;\n userEntries: number;\n assistantEntries: number;\n oldestTimestamp: number | null;\n newestTimestamp: number | null;\n } {\n if (this.contextHistory.length === 0) {\n return {\n totalEntries: 0,\n userEntries: 0,\n assistantEntries: 0,\n oldestTimestamp: null,\n newestTimestamp: null,\n };\n }\n\n const userEntries = this.contextHistory.filter(entry => entry.role === 'user').length;\n const assistantEntries = this.contextHistory.filter(entry => entry.role === 'assistant').length;\n const timestamps = this.contextHistory.map(entry => entry.timestamp);\n\n return {\n totalEntries: this.contextHistory.length,\n userEntries,\n assistantEntries,\n oldestTimestamp: Math.min(...timestamps),\n newestTimestamp: Math.max(...timestamps),\n };\n }\n\n /**\n * Search context for specific content\n */\n searchContext(query: string, role?: 'user' | 'assistant'): ContextEntry[] {\n const searchQuery = query.toLowerCase();\n\n return this.contextHistory.filter(entry => {\n const matchesRole = role ? entry.role === role : true;\n const matchesContent = entry.content.toLowerCase().includes(searchQuery);\n\n return matchesRole && matchesContent;\n });\n }\n\n /**\n * Get recent context entries\n */\n getRecentEntries(count: number): ContextEntry[] {\n return this.contextHistory.slice(-count);\n }\n\n /**\n * Get context entries by role\n */\n getEntriesByRole(role: 'user' | 'assistant'): ContextEntry[] {\n return this.contextHistory.filter(entry => entry.role === role);\n }\n}\n","import { GoogleAuth } from 'google-auth-library';\nimport { GeminiLiveErrorCode } from '../types';\nimport type { AuthOptions } from '../types';\nimport { GeminiLiveError } from '../utils/errors';\n\nexport interface AuthConfig {\n apiKey?: string;\n vertexAI?: boolean;\n project?: string;\n debug: boolean;\n serviceAccountKeyFile?: string;\n serviceAccountEmail?: string;\n tokenExpirationTime?: number;\n}\n\nexport class AuthManager {\n private authClient?: GoogleAuth;\n private accessToken?: string;\n private tokenExpirationTime?: number;\n private readonly config: AuthConfig;\n\n constructor(config: AuthConfig) {\n this.config = config;\n this.tokenExpirationTime = config.tokenExpirationTime ?? 50 * 60 * 1000;\n }\n\n /**\n * Initialize authentication based on configuration\n */\n async initialize(): Promise<void> {\n if (this.config.vertexAI) {\n await this.initializeVertexAI();\n } else if (this.config.apiKey) {\n // API key auth doesn't need initialization\n return;\n } else {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.API_KEY_MISSING,\n 'Either API key or Vertex AI configuration is required',\n );\n }\n }\n\n /**\n * Initialize Vertex AI authentication\n */\n private async initializeVertexAI(): Promise<void> {\n if (!this.config.project) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.PROJECT_ID_MISSING,\n 'Google Cloud project ID is required when using Vertex AI',\n );\n }\n\n const authOptions: AuthOptions = {\n scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n projectId: this.config.project,\n };\n\n // Support service account key file\n if (this.config.serviceAccountKeyFile) {\n authOptions.keyFilename = this.config.serviceAccountKeyFile;\n this.log('Using service account key file for authentication:', this.config.serviceAccountKeyFile);\n }\n\n // Support impersonation via service account email\n if (this.config.serviceAccountEmail) {\n authOptions.clientOptions = { subject: this.config.serviceAccountEmail };\n this.log('Using service account impersonation:', this.config.serviceAccountEmail);\n }\n\n try {\n this.authClient = new GoogleAuth(authOptions);\n } catch (error) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.AUTHENTICATION_FAILED,\n `Failed to initialize Vertex AI authentication: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Get access token for Vertex AI\n */\n async getAccessToken(): Promise<string> {\n if (!this.config.vertexAI) {\n throw new GeminiLiveError(GeminiLiveErrorCode.AUTHENTICATION_FAILED, 'Vertex AI authentication not configured');\n }\n\n if (!this.authClient) {\n throw new GeminiLiveError(GeminiLiveErrorCode.AUTHENTICATION_FAILED, 'Authentication client not initialized');\n }\n\n // Check if we have a valid cached token\n if (this.accessToken && this.tokenExpirationTime && Date.now() < this.tokenExpirationTime) {\n return this.accessToken;\n }\n\n try {\n const client = await this.authClient.getClient();\n const token = await client.getAccessToken();\n\n if (!token.token) {\n throw new Error('No access token received');\n }\n\n this.accessToken = token.token;\n\n // Set expiry time (tokens typically last 1 hour, so set to 50 minutes to be safe)\n this.tokenExpirationTime = Date.now() + 50 * 60 * 1000;\n\n this.log('Successfully obtained new access token');\n return this.accessToken;\n } catch (error) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.AUTHENTICATION_FAILED,\n `Failed to get access token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Get API key if using API key authentication\n */\n getApiKey(): string | undefined {\n if (this.config.vertexAI) {\n return undefined;\n }\n return this.config.apiKey;\n }\n\n /**\n * Check if using Vertex AI authentication\n */\n isUsingVertexAI(): boolean {\n return this.config.vertexAI === true;\n }\n\n /**\n * Check if authentication is configured\n */\n isConfigured(): boolean {\n return !!(this.config.apiKey || (this.config.vertexAI && this.config.project));\n }\n\n /**\n * Check if access token is valid\n */\n hasValidToken(): boolean {\n if (!this.config.vertexAI) return false;\n return !!(this.accessToken && this.tokenExpirationTime && Date.now() < this.tokenExpirationTime);\n }\n\n /**\n * Clear cached authentication data\n */\n clearCache(): void {\n this.accessToken = undefined;\n this.tokenExpirationTime = undefined;\n }\n\n /**\n * Get authentication configuration\n */\n getConfig(): AuthConfig {\n return { ...this.config };\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.config.debug) {\n console.info(`[AuthManager] ${message}`, ...args);\n }\n }\n}\n","import { EventEmitter } from 'node:events';\n// Make the event manager generic over an event map\nexport type EventMap = Record<string, unknown>;\n\nexport interface EventConfig {\n debug: boolean;\n}\n\nexport class EventManager<TEvents extends EventMap = Record<string, unknown>> {\n private eventEmitter: EventEmitter;\n private readonly debug: boolean;\n private eventCounts: Record<string, number> = {};\n\n constructor(config: EventConfig) {\n this.eventEmitter = new EventEmitter();\n this.debug = config.debug;\n }\n\n /**\n * Emit an event with data\n */\n emit<K extends Extract<keyof TEvents, string>>(event: K, data: TEvents[K]): boolean {\n this.incrementEventCount(event);\n const result = this.eventEmitter.emit(event, data);\n\n if (this.debug) {\n this.log(`Emitted event: ${event}`, data);\n }\n\n return result;\n }\n\n /**\n * Add event listener\n */\n on<E extends Extract<keyof TEvents, string>>(event: E, callback: (data: TEvents[E]) => void): void {\n this.eventEmitter.on(event, callback);\n\n if (this.debug) {\n this.log(`Added listener for event: ${event}`);\n }\n }\n\n /**\n * Remove event listener\n */\n off<E extends Extract<keyof TEvents, string>>(event: E, callback: (data: TEvents[E]) => void): void {\n this.eventEmitter.off(event, callback);\n\n if (this.debug) {\n this.log(`Removed listener for event: ${event}`);\n }\n }\n\n /**\n * Add one-time event listener\n */\n once<E extends Extract<keyof TEvents, string>>(event: E, callback: (data: TEvents[E]) => void): void {\n this.eventEmitter.once(event, callback);\n\n if (this.debug) {\n this.log(`Added one-time listener for event: ${event}`);\n }\n }\n\n /**\n * Remove all listeners for an event\n */\n removeAllListeners(event?: string): void {\n this.eventEmitter.removeAllListeners(event);\n\n if (this.debug) {\n this.log(`Removed all listeners${event ? ` for event: ${event}` : ''}`);\n }\n }\n\n /**\n * Get event listener count\n */\n getListenerCount(event: string): number {\n return this.eventEmitter.listenerCount(event);\n }\n\n /**\n * Get event listener info\n */\n getEventListenerInfo(): Record<string, number> {\n const events = this.eventEmitter.eventNames();\n const info: Record<string, number> = {};\n\n events.forEach(event => {\n const eventName = typeof event === 'string' ? event : event.toString();\n info[eventName] = this.eventEmitter.listenerCount(event);\n });\n\n return info;\n }\n\n /**\n * Get event emission counts\n */\n getEventCounts(): Record<string, number> {\n return { ...this.eventCounts };\n }\n\n /**\n * Reset event counts\n */\n resetEventCounts(): void {\n this.eventCounts = {};\n }\n\n /**\n * Clean up event listeners\n */\n cleanup(): void {\n this.eventEmitter.removeAllListeners();\n this.resetEventCounts();\n\n if (this.debug) {\n this.log('Cleaned up all event listeners');\n }\n }\n\n /**\n * Get the underlying EventEmitter\n */\n getEventEmitter(): EventEmitter {\n return this.eventEmitter;\n }\n\n /**\n * Increment event count for tracking\n */\n private incrementEventCount(event: string): void {\n this.eventCounts[event] = (this.eventCounts[event] || 0) + 1;\n }\n\n /**\n * Log message if debug is enabled\n */\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[EventManager] ${message}`, ...args);\n }\n }\n}\n","import { MastraVoice } from '@internal/voice';\nimport type { ToolsInput, VoiceEventType, VoiceConfig } from '@internal/voice';\nimport { GoogleSchemaCompatLayer } from '@mastra/schema-compat';\nimport type { WebSocket as WSType } from 'ws';\nimport { WebSocket } from 'ws';\nimport { AudioStreamManager, ConnectionManager, ContextManager, AuthManager, EventManager } from './managers';\nimport { GeminiLiveErrorCode } from './types';\nimport type {\n GeminiLiveVoiceConfig,\n GeminiLiveVoiceOptions,\n GeminiLiveEventMap,\n GeminiVoiceModel,\n GeminiVoiceName,\n GeminiToolConfig,\n AudioConfig,\n GeminiLiveServerMessage,\n GeminiSessionConfig,\n IncrementalTurn,\n UpdateMessage,\n} from './types';\nimport { GeminiLiveError } from './utils/errors';\n\n// Narrow event keys to strings for the typed EventManager\ntype GeminiEventName = Extract<keyof GeminiLiveEventMap, string>;\n\n/**\n * Default configuration values\n */\nconst DEFAULT_MODEL: GeminiVoiceModel = 'gemini-3.1-flash-live-preview';\nconst DEFAULT_VOICE: GeminiVoiceName = 'Puck';\n\n// How many recently handled tool call ids to keep for duplicate suppression.\n// Insertion-ordered; the oldest id is evicted once the bound is reached.\nconst MAX_TRACKED_TOOL_CALL_IDS = 512;\n\n// Treats only plain objects (own prototype chain ends at `Object.prototype` or `null`) as\n// proto-struct compatible — `Date`, `Map`, `Set`, `Error`, `RegExp`, and class instances all\n// JSON-serialize to `{}` if forwarded bare and need wrapping for Gemini Live's `response` field.\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/**\n * Helper class for consistent error handling\n */\n// GeminiLiveError is now defined in types.ts for reuse across managers\n\n/**\n * GeminiLiveVoice provides real-time multimodal voice interactions using Google's Gemini Live API.\n *\n * Features:\n * - Bidirectional audio streaming\n * - Built-in VAD and interrupt handling\n * - Tool calling capabilities\n * - Session management and resumption\n * - Live transcription\n * - Support for both Gemini API and Vertex AI\n *\n * @example Backward compatibility - Direct options (legacy)\n * ```typescript\n * const voice = new GeminiLiveVoice({\n * apiKey: 'your-api-key',\n * model: 'gemini-2.0-flash-live-001',\n * speaker: 'Puck',\n * instructions: 'You are a helpful assistant'\n * });\n * ```\n *\n * @example Mastra VoiceConfig pattern - Recommended\n * ```typescript\n * const voice = new GeminiLiveVoice({\n * speechModel: { name: 'gemini-2.0-flash-live-001', apiKey: 'your-api-key' },\n * speaker: 'Puck',\n * realtimeConfig: {\n * model: 'gemini-2.0-flash-live-001',\n * apiKey: 'your-api-key',\n * options: {\n * instructions: 'You are a helpful assistant',\n * debug: true\n * }\n * }\n * });\n * ```\n *\n * @example Using Vertex AI (with OAuth)\n * ```typescript\n * const voice = new GeminiLiveVoice({\n * realtimeConfig: {\n * model: 'gemini-2.0-flash-live-001',\n * options: {\n * vertexAI: true,\n * project: 'your-gcp-project',\n * location: 'us-central1',\n * serviceAccountKeyFile: '/path/to/service-account.json',\n * }\n * }\n * });\n * ```\n */\nexport class GeminiLiveVoice extends MastraVoice<\n GeminiLiveVoiceConfig,\n GeminiLiveVoiceOptions,\n GeminiLiveVoiceOptions,\n ToolsInput,\n GeminiLiveEventMap\n> {\n private ws?: WSType;\n private eventManager: EventManager;\n private state: 'disconnected' | 'connected' = 'disconnected';\n private sessionHandle?: string;\n private readonly debug: boolean;\n private readonly audioConfig: AudioConfig;\n private queue: unknown[] = [];\n\n // Managers\n private connectionManager: ConnectionManager;\n private contextManager: ContextManager;\n private authManager: AuthManager;\n\n // Audio chunk concatenation - optimized stream management\n private audioStreamManager: AudioStreamManager;\n\n // Session management properties\n private sessionId?: string;\n private sessionStartTime?: number;\n private isResuming = false;\n private sessionDurationTimeout?: NodeJS.Timeout;\n private sessionExpiringTimeout?: NodeJS.Timeout;\n\n // Tool integration properties\n private tools?: ToolsInput;\n private requestContext?: any;\n // Provider call ids already handled on this connection. Gemini Live can deliver the same\n // function call through both inbound representations — a top-level `toolCall` message and\n // `serverContent.modelTurn.parts[].functionCall` — so deduplicate by call id to execute\n // each call exactly once. Calls without a provider id get a fresh UUID and are never\n // suppressed.\n private processedToolCallIds = new Set<string>();\n\n // Store the configuration options\n private options: GeminiLiveVoiceConfig;\n\n // Accumulates assistant text across `serverContent` frames for the current\n // turn. Live API streams responses over many frames; we aggregate here and\n // flush to context history once on `turnComplete`.\n private pendingAssistantResponse = '';\n\n /**\n * Normalize configuration to ensure proper VoiceConfig format\n * Handles backward compatibility with direct GeminiLiveVoiceConfig\n * @private\n */\n private static normalizeConfig(\n config: VoiceConfig<GeminiLiveVoiceConfig> | GeminiLiveVoiceConfig,\n ): VoiceConfig<GeminiLiveVoiceConfig> {\n // Check if this is already a proper VoiceConfig (has realtimeConfig or standard VoiceConfig properties)\n if ('realtimeConfig' in config || 'speechModel' in config || 'listeningModel' in config) {\n return config as VoiceConfig<GeminiLiveVoiceConfig>;\n }\n\n // Convert direct GeminiLiveVoiceConfig to VoiceConfig format\n const geminiConfig = config as GeminiLiveVoiceConfig;\n return {\n speechModel: {\n name: geminiConfig.model || DEFAULT_MODEL,\n apiKey: geminiConfig.apiKey,\n },\n speaker: geminiConfig.speaker || DEFAULT_VOICE,\n realtimeConfig: {\n model: geminiConfig.model || DEFAULT_MODEL,\n apiKey: geminiConfig.apiKey,\n options: geminiConfig,\n },\n };\n }\n\n /**\n * Creates a new GeminiLiveVoice instance\n *\n * @param config Configuration options\n */\n constructor(config: VoiceConfig<GeminiLiveVoiceConfig> | GeminiLiveVoiceConfig = {}) {\n // Handle backward compatibility - if config has Gemini-specific properties directly,\n // convert to proper VoiceConfig format\n const normalizedConfig = GeminiLiveVoice.normalizeConfig(config);\n super(normalizedConfig);\n\n // Seed `this.options` from `realtimeConfig`. Fields on the `realtimeConfig` root\n // (`model`, `apiKey`, `vertexAI`, `project`, etc.) and on the inner `options` object both\n // belong in the flat `GeminiLiveVoiceConfig` shape this class reads from; merge with the\n // explicit inner `options` last so caller intent on the inner object wins on collisions.\n const realtimeConfig = normalizedConfig.realtimeConfig;\n if (realtimeConfig) {\n const { options: innerOptions, ...realtimeConfigRoot } = realtimeConfig;\n this.options = { ...(realtimeConfigRoot as Partial<GeminiLiveVoiceConfig>), ...(innerOptions || {}) };\n } else {\n this.options = {};\n }\n\n // `speaker` lives at the `VoiceConfig` root, sibling to `realtimeConfig` — not inside it.\n // Propagate explicitly so `new GeminiLiveVoice({ speaker: 'Puck', realtimeConfig: { ... } })`\n // honors the caller's voice. Reading from `normalizedConfig.speaker` would pin `DEFAULT_VOICE`\n // whenever the flat-config branch normalized without an explicit speaker, so use the raw `config`.\n if ('realtimeConfig' in config && config.speaker && !this.options.speaker) {\n this.options.speaker = config.speaker as GeminiVoiceName;\n }\n\n // Validate API key\n const apiKey = this.options.apiKey;\n if (!apiKey && !this.options.vertexAI) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.API_KEY_MISSING,\n 'Google API key is required. Set GOOGLE_API_KEY environment variable or pass apiKey to constructor',\n );\n }\n\n this.debug = this.options.debug || false;\n\n // Merge provided audio config with defaults\n this.audioConfig = {\n ...AudioStreamManager.getDefaultAudioConfig(),\n ...this.options.audioConfig,\n };\n\n // Initialize AudioStreamManager\n this.audioStreamManager = new AudioStreamManager(this.audioConfig, this.debug);\n // Inject sender so AudioStreamManager can deliver realtime audio\n this.audioStreamManager.setSender((type, message) => this.sendEvent(type, message));\n\n this.eventManager = new EventManager<GeminiLiveEventMap>({ debug: this.debug });\n this.connectionManager = new ConnectionManager({ debug: this.debug, timeoutMs: 30000 });\n this.contextManager = new ContextManager({\n maxEntries: 100,\n compressionThreshold: 50,\n compressionEnabled: this.options.sessionConfig?.contextCompression ?? false,\n });\n this.authManager = new AuthManager({\n apiKey: this.options.apiKey,\n vertexAI: this.options.vertexAI,\n project: this.options.project,\n serviceAccountKeyFile: this.options.serviceAccountKeyFile,\n serviceAccountEmail: this.options.serviceAccountEmail,\n debug: this.debug,\n tokenExpirationTime: this.options.tokenExpirationTime,\n });\n\n if (this.options.vertexAI && !this.options.project) {\n throw new GeminiLiveError(\n GeminiLiveErrorCode.PROJECT_ID_MISSING,\n 'Google Cloud project ID is required when using Vertex AI. Set GOOGLE_CLOUD_PROJECT environment variable or pass project to constructor',\n );\n }\n\n // Auth initialization handled by AuthManager during connect\n }\n\n /**\n * Register an event listener\n * @param event Event name (e.g., 'speaking', 'writing', 'error', 'speaker')\n * @param callback Callback function that receives event data\n *\n * @example\n * ```typescript\n * // Listen for audio responses\n * voice.on('speaking', ({ audio, audioData, sampleRate }) => {\n * console.log('Received audio chunk:', audioData.length);\n * });\n *\n * // Listen for text responses and transcriptions\n * voice.on('writing', ({ text, role }) => {\n * console.log(`${role}: ${text}`);\n * });\n *\n * // Listen for audio streams (for concatenated playback)\n * voice.on('speaker', (audioStream) => {\n * audioStream.pipe(playbackDevice);\n * });\n *\n * // Handle errors\n * voice.on('error', ({ message, code, details }) => {\n * console.error('Voice error:', message);\n * });\n * ```\n */\n on<E extends VoiceEventType>(\n event: E,\n callback: (data: E extends keyof GeminiLiveEventMap ? GeminiLiveEventMap[E] : unknown) => void,\n ): void {\n try {\n this.eventManager.on(event as GeminiEventName, callback as any);\n this.log(`Event listener registered for: ${event}`);\n } catch (error) {\n this.log(`Failed to register event listener for ${event}:`, error);\n throw error;\n }\n }\n\n /**\n * Remove an event listener\n * @param event Event name\n * @param callback Callback function to remove\n */\n off<E extends VoiceEventType>(\n event: E,\n callback: (data: E extends keyof GeminiLiveEventMap ? GeminiLiveEventMap[E] : unknown) => void,\n ): void {\n try {\n this.eventManager.off(event as GeminiEventName, callback as any);\n this.log(`Event listener removed for: ${event}`);\n } catch (error) {\n this.log(`Failed to remove event listener for ${event}:`, error);\n }\n }\n\n /**\n * Register a one-time event listener that automatically removes itself after the first emission\n * @param event Event name\n * @param callback Callback function that receives event data\n */\n once<E extends VoiceEventType>(\n event: E,\n callback: (data: E extends keyof GeminiLiveEventMap ? GeminiLiveEventMap[E] : unknown) => void,\n ): void {\n try {\n this.eventManager.once(event as GeminiEventName, callback as any);\n this.log(`One-time event listener registered for: ${event}`);\n } catch (error) {\n this.log(`Failed to register one-time event listener for ${event}:`, error);\n throw error;\n }\n }\n\n /**\n * Emit an event to listeners with improved error handling\n * @private\n */\n private emit<K extends keyof GeminiLiveEventMap>(event: K, data: GeminiLiveEventMap[K]): boolean {\n try {\n const listenerCount = this.eventManager.getListenerCount(event as string);\n if (listenerCount === 0 && this.debug) {\n this.log(`No listeners for event: ${String(event)}`);\n }\n\n const result = this.eventManager.emit(event as GeminiEventName, data as any);\n\n if (this.debug && listenerCount > 0) {\n this.log(`Emitted event: ${String(event)} to ${listenerCount} listeners`);\n }\n\n return result;\n } catch (error) {\n this.log(`Error emitting event ${String(event)}:`, error);\n\n // Emit error event if this wasn't already an error event (prevent infinite loops)\n if (event !== 'error') {\n try {\n // Use underlying emitter directly to avoid recursion\n this.eventManager.getEventEmitter().emit('error', {\n message: `Failed to emit event: ${String(event)}`,\n code: 'event_emission_error',\n details: error,\n });\n } catch (nestedError) {\n // If we can't even emit the error event, log it\n this.log('Critical: Failed to emit error event:', nestedError);\n }\n }\n\n return false;\n }\n }\n\n /**\n * Clean up event listeners to prevent memory leaks\n * @private\n */\n private cleanupEventListeners(): void {\n try {\n // Get current listener counts for debugging\n const events = this.eventManager.getEventEmitter().eventNames();\n if (this.debug && events.length > 0) {\n this.log(\n 'Cleaning up event listeners:',\n events.map(event => `${String(event)}: ${this.eventManager.getListenerCount(String(event))}`).join(', '),\n );\n }\n\n // Remove all listeners\n this.eventManager.cleanup();\n\n this.log('Event listeners cleaned up');\n } catch (error) {\n this.log('Error cleaning up event listeners:', error);\n }\n }\n\n /**\n * Get current event listener information for debugging\n * @returns Object with event names and listener counts\n */\n getEventListenerInfo(): Record<string, number> {\n try {\n return this.eventManager.getEventListenerInfo();\n } catch (error) {\n this.log('Error getting event listener info:', error);\n return {} as Record<string, number>;\n }\n }\n\n /**\n * Create and emit a standardized error\n * @private\n */\n private createAndEmitError(code: GeminiLiveErrorCode, message: string, details?: unknown): GeminiLiveError {\n const error = new GeminiLiveError(code, message, details);\n this.log(`Error [${code}]: ${message}`, details);\n this.emit('error', error.toEventData());\n return error;\n }\n\n /**\n * Handle connection state validation with standardized errors\n * @private\n */\n private validateConnectionState(): void {\n if (this.state !== 'connected') {\n throw this.createAndEmitError(\n GeminiLiveErrorCode.NOT_CONNECTED,\n 'Not connected to Gemini Live API. Call connect() first.',\n { currentState: this.state },\n );\n }\n }\n\n /**\n * Handle WebSocket state validation with standardized errors\n * @private\n */\n private validateWebSocketState(): void {\n if (!this.connectionManager.isConnected()) {\n throw this.createAndEmitError(GeminiLiveErrorCode.WEBSOCKET_ERROR, 'WebSocket is not open', {\n wsExists: !!this.connectionManager.getWebSocket(),\n readyState: this.connectionManager.getWebSocket()?.readyState,\n expectedState: WebSocket.OPEN,\n });\n }\n }\n\n /**\n * Establish connection to the Gemini Live API\n */\n async connect({ requestContext }: { requestContext?: any } = {}): Promise<void> {\n if (this.state === 'connected') {\n this.log('Already connected to Gemini Live API');\n return;\n }\n\n // Dedup state must not leak across connections: ids used by a prior\n // connection are valid again once a fresh one is opened.\n this.processedToolCallIds.clear();\n\n // Store request context for tool execution\n this.requestContext = requestContext;\n\n // Emit connecting event\n this.emit('session', { state: 'connecting' });\n\n try {\n // Build WebSocket URL based on official Gemini Live API documentation\n let wsUrl: string;\n let headers: WebSocket.ClientOptions = {};\n\n if (this.options.vertexAI) {\n const location = this.getVertexLocation();\n // Vertex AI endpoint - using correct LlmBidiService endpoint\n wsUrl = `wss://${location}-aiplatform.googleapis.com/ws/google.cloud.aiplatform.v1beta1.LlmBidiService/BidiGenerateContent`;\n // Initialize auth and get token\n await this.authManager.initialize();\n const accessToken = await this.authManager.getAccessToken();\n headers = { headers: { Authorization: `Bearer ${accessToken}` } };\n this.log('Using Vertex AI authentication with OAuth token');\n } else {\n // Live API endpoint - this is specifically for the Live API\n wsUrl = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent`;\n headers = {\n headers: {\n 'x-goog-api-key': this.options.apiKey || '',\n 'Content-Type': 'application/json',\n },\n };\n this.log('Using Live API authentication with API key');\n }\n\n this.log('Connecting to:', wsUrl);\n this.ws = new WebSocket(wsUrl, undefined, headers);\n this.connectionManager.setWebSocket(this.ws);\n\n this.setupEventListeners();\n\n // Wait for WebSocket connection to open via ConnectionManager\n await this.connectionManager.waitForOpen();\n\n // Send initial configuration (session_resumption handle is embedded in setup frame)\n this.sendInitialConfig();\n this.sessionStartTime = Date.now();\n this.sessionId = globalThis.crypto.randomUUID();\n\n // Wait for session to be created after sending config\n await this.waitForSessionCreated();\n\n this.state = 'connected';\n\n // Emit session connected event\n this.emit('session', {\n state: 'connected',\n config: {\n sessionId: this.sessionId,\n isResuming: this.isResuming,\n toolCount: Object.keys(this.tools || {}).length,\n },\n });\n\n this.log('Successfully connected to Gemini Live API', {\n sessionId: this.sessionId,\n isResuming: this.isResuming,\n toolCount: Object.keys(this.tools || {}).length,\n });\n\n // Start session duration monitoring if configured\n if (this.options.sessionConfig?.maxDuration) {\n this.startSessionDurationMonitor();\n }\n } catch (error) {\n this.state = 'disconnected';\n this.log('Connection failed', error);\n throw error;\n }\n }\n\n /**\n * Disconnect from the Gemini Live API\n */\n async disconnect(): Promise<void> {\n if (this.state === 'disconnected') {\n this.log('Already disconnected');\n return;\n }\n\n // Emit disconnecting event\n this.emit('session', { state: 'disconnecting' });\n\n // Clean up session duration monitoring\n this.clearSessionTimers();\n\n // Session handle is set by the server via sessionResumptionUpdate frames.\n // Do not overwrite it with the client-side sessionId here.\n\n if (this.ws) {\n this.connectionManager.close();\n this.ws = undefined;\n }\n\n // Clean up speaker streams with improved handling\n this.audioStreamManager.cleanupSpeakerStreams();\n\n // Clear cached OAuth token via AuthManager\n this.authManager.clearCache();\n\n this.state = 'disconnected';\n this.isResuming = false;\n\n // Emit final session event before cleanup\n this.emit('session', { state: 'disconnected' });\n\n // Clean up event listeners to prevent memory leaks\n this.cleanupEventListeners();\n\n this.log('Disconnected from Gemini Live API', {\n sessionId: this.sessionId,\n sessionDuration: this.sessionStartTime ? Date.now() - this.sessionStartTime : undefined,\n });\n }\n\n /**\n * Send text to be converted to speech\n */\n async speak(input: string | NodeJS.ReadableStream, options?: GeminiLiveVoiceOptions): Promise<void> {\n this.validateConnectionState();\n\n if (typeof input !== 'string') {\n const chunks: Buffer[] = [];\n for await (const chunk of input) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));\n }\n input = Buffer.concat(chunks).toString('utf-8');\n }\n\n if (input.trim().length === 0) {\n throw this.createAndEmitError(GeminiLiveErrorCode.INVALID_AUDIO_FORMAT, 'Input text is empty');\n }\n\n // Add to context history\n this.addToContext('user', input);\n\n // Build text message to Gemini Live API\n const textMessage: any = {\n client_content: {\n turns: [\n {\n role: 'user',\n parts: [\n {\n text: input,\n },\n ],\n },\n ],\n turnComplete: true,\n },\n };\n\n // If runtime options provided, send a session.update first to apply per-turn settings\n if (options && (options.speaker || options.languageCode || options.responseModalities)) {\n const updateMessage: UpdateMessage = {\n type: 'session.update',\n session: {\n generation_config: {\n ...(options.responseModalities ? { response_modalities: options.responseModalities } : {}),\n speech_config: {\n ...(options.languageCode ? { language_code: options.languageCode } : {}),\n ...(options.speaker ? { voice_config: { prebuilt_voice_config: { voice_name: options.speaker } } } : {}),\n },\n },\n },\n };\n\n try {\n this.sendEvent('session.update', updateMessage);\n this.log('Applied per-turn runtime options', options);\n } catch (error) {\n this.log('Failed to apply per-turn runtime options', error);\n }\n }\n\n try {\n this.sendEvent('client_content', textMessage);\n this.log('Text message sent', { text: input });\n\n // The response will come via the event system (handleServerContent)\n // Audio will be emitted through 'speaking' events\n // Text responses will be emitted through 'writing' events\n } catch (error) {\n this.log('Failed to send text message', error);\n throw this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Failed to send text message', error);\n }\n }\n\n /**\n * Send conversation history into the live session without triggering a model response.\n *\n * Maps to a single Gemini Live `client_content` frame with `turnComplete` defaulting\n * to `false`, which loads the turns into context silently. The model only responds\n * once a subsequent turn completes (e.g. via {@link speak} or user audio).\n *\n * @param turns Prior conversation turns to seed into the session.\n * @param options.turnComplete Whether to mark the turn as complete (default `false`).\n *\n * @example\n * ```typescript\n * await voice.connect();\n *\n * // Replay prior conversation without triggering a reply.\n * await voice.sendContext([\n * { role: 'user', content: 'What is the weather?' },\n * { role: 'assistant', content: 'It is 72°F in San Francisco.' },\n * ]);\n *\n * // Agent stays silent until the user actually speaks.\n * await voice.send(micStream);\n * ```\n */\n async sendContext(turns: IncrementalTurn[], options?: { turnComplete?: boolean }): Promise<void> {\n this.validateConnectionState();\n\n if (!turns || turns.length === 0) {\n this.log('sendContext called with empty turns, skipping');\n return;\n }\n\n const message = {\n client_content: {\n turns: turns.map(t => ({\n role: t.role === 'assistant' ? 'model' : 'user',\n parts: [{ text: t.content }],\n })),\n turnComplete: options?.turnComplete ?? false,\n },\n };\n\n try {\n this.sendEvent('client_content', message);\n this.log('Context seeded', { turnCount: turns.length, turnComplete: options?.turnComplete ?? false });\n\n for (const turn of turns) {\n this.addToContext(turn.role, turn.content);\n }\n } catch (error) {\n this.log('Failed to send context', error);\n throw this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Failed to send context', error);\n }\n }\n\n /**\n * Send audio stream for processing\n */\n async send(audioData: NodeJS.ReadableStream | Int16Array): Promise<void> {\n this.validateConnectionState();\n\n if ('readable' in audioData && typeof audioData.on === 'function') {\n const stream = audioData as NodeJS.ReadableStream;\n\n stream.on('data', (chunk: Buffer) => {\n try {\n const base64Audio = this.audioStreamManager.processAudioChunk(chunk);\n const message = this.audioStreamManager.createAudioMessage(base64Audio, 'realtime');\n this.sendEvent('realtime_input', message);\n } catch (error) {\n this.log('Failed to process audio chunk', error);\n this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Failed to process audio chunk', error);\n }\n });\n\n stream.on('error', (error: Error) => {\n this.log('Audio stream error', error);\n this.createAndEmitError(GeminiLiveErrorCode.AUDIO_STREAM_ERROR, 'Audio stream error', error);\n });\n\n stream.on('end', () => {\n this.log('Audio stream ended');\n });\n } else {\n const validateAudio = this.audioStreamManager.validateAndConvertAudioInput(audioData as Int16Array);\n const base64Audio = this.audioStreamManager.int16ArrayToBase64(validateAudio);\n const message = this.audioStreamManager.createAudioMessage(base64Audio, 'realtime');\n this.sendEvent('realtime_input', message);\n }\n }\n\n /**\n * Process speech from audio stream (traditional STT interface)\n */\n async listen(audioStream: NodeJS.ReadableStream, _options?: GeminiLiveVoiceOptions): Promise<string> {\n this.validateConnectionState();\n\n let transcriptionText = '';\n\n // Listen for transcription responses\n const onWriting = (data: { text: string; role: 'assistant' | 'user' }) => {\n if (data.role === 'user') {\n transcriptionText += data.text;\n this.log('Received transcription text:', { text: data.text, total: transcriptionText });\n }\n // Note: We only collect user role text as transcription\n // Assistant role text would be responses, not transcription\n };\n\n // Listen for errors\n const onError = (error: { message: string; code?: string; details?: unknown }) => {\n throw new Error(`Transcription failed: ${error.message}`);\n };\n\n // Listen for session events\n const onSession = (data: { state: string }) => {\n if (data.state === 'disconnected') {\n throw new Error('Session disconnected during transcription');\n }\n };\n\n // Set up GeminiLiveVoice event listeners\n this.on('writing', onWriting);\n this.on('error', onError);\n this.on('session', onSession);\n\n try {\n // Use AudioStreamManager to handle the transcription workflow\n const result = await this.audioStreamManager.handleAudioTranscription(\n audioStream,\n (base64Audio: string) => {\n // Send audio and await transcript until turn completes\n return new Promise<string>((resolve, reject) => {\n try {\n // Create audio message for transcription\n const message = this.audioStreamManager.createAudioMessage(base64Audio, 'input');\n\n const cleanup = () => {\n this.off('turnComplete' as any, onTurnComplete as any);\n this.off('error', onErr as any);\n };\n\n // Handlers\n const onTurnComplete = () => {\n cleanup();\n resolve(transcriptionText.trim());\n };\n\n const onErr = (e: { message: string }) => {\n cleanup();\n reject(new Error(e.message));\n };\n\n // Wire listeners before sending\n this.on('turnComplete' as any, onTurnComplete as any);\n this.on('error', onErr as any);\n\n // Send to Gemini Live API\n this.sendEvent('client_content', message);\n this.log('Sent audio for transcription');\n } catch (err) {\n reject(err as Error);\n }\n });\n },\n (error: Error) => {\n this.createAndEmitError(GeminiLiveErrorCode.AUDIO_PROCESSING_ERROR, 'Audio transcription failed', error);\n },\n );\n\n return result;\n } finally {\n // Clean up event listeners\n this.off('writing', onWriting);\n this.off('error', onError);\n this.off('session', onSession);\n }\n }\n\n /**\n * Get available speakers/voices\n */\n async getSpeakers(): Promise<Array<{ voiceId: string; description?: string }>> {\n // Return available Gemini Live voices\n return [\n { voiceId: 'Puck', description: 'Conversational, friendly' },\n { voiceId: 'Charon', description: 'Deep, authoritative' },\n { voiceId: 'Kore', description: 'Neutral, professional' },\n { voiceId: 'Fenrir', description: 'Warm, approachable' },\n ];\n }\n\n /**\n * Resume a previous session using a session handle\n */\n async resumeSession(handle: string, context?: Array<{ role: string; content: string }>): Promise<void> {\n if (this.state === 'connected') {\n throw new Error('Cannot resume session while already connected. Disconnect first.');\n }\n\n this.log('Attempting to resume session', { handle });\n\n this.sessionHandle = handle;\n this.isResuming = true;\n\n // Restore context history if provided using ContextManager\n if (context && context.length > 0) {\n this.contextManager.clearContext();\n for (const item of context) {\n this.contextManager.addEntry(item.role as 'user' | 'assistant', item.content);\n }\n }\n\n try {\n await this.connect();\n this.log('Session resumed successfully', { handle, contextItems: context?.length || 0 });\n } catch (error) {\n this.isResuming = false;\n this.sessionHandle = undefined;\n throw new Error(`Failed to resume session: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Update session configuration during an active session\n * Allows dynamic updates to voice, instructions, tools, and other settings\n *\n * @param config Partial configuration to update\n * @throws Error if not connected or update fails\n *\n * @example\n * ```typescript\n * // Change voice during conversation\n * await voice.updateSessionConfig({\n * speaker: 'Charon'\n * });\n *\n * // Update instructions\n * await voice.updateSessionConfig({\n * instructions: 'You are now a helpful coding assistant'\n * });\n *\n * // Add or update tools\n * await voice.updateSessionConfig({\n * tools: [{ name: 'new_tool', ... }]\n * });\n * ```\n */\n async updateSessionConfig(config: Partial<GeminiLiveVoiceConfig>): Promise<void> {\n this.validateConnectionState();\n this.validateWebSocketState();\n\n return new Promise((resolve, reject) => {\n // Validate configuration\n if (config.model) {\n this.log('Warning: Model cannot be changed during an active session. Ignoring model update.');\n }\n\n if (config.vertexAI !== undefined || config.project !== undefined || config.location !== undefined) {\n this.log('Warning: Authentication settings cannot be changed during an active session.');\n }\n\n const updateMessage: UpdateMessage = {\n type: 'session.update',\n session: {},\n };\n\n let hasUpdates = false;\n\n // Update voice/speaker if provided\n if (config.speaker) {\n hasUpdates = true;\n updateMessage.session.generation_config = {\n ...updateMessage.session.generation_config,\n speech_config: {\n voice_config: {\n prebuilt_voice_config: {\n voice_name: config.speaker,\n },\n },\n },\n };\n\n // Update internal state\n this.speaker = config.speaker;\n this.log('Updating speaker to:', config.speaker);\n }\n\n // Update thinking configuration if provided. Mirrors `sendInitialConfig`: public config is\n // camelCase, the wire format is snake_case. Merge into any existing generation_config so a\n // speaker + thinkingConfig update in the same call don't clobber each other. Only emit when a\n // sub-field is set, so an empty `thinkingConfig: {}` is a no-op rather than a bare object.\n const tc = config.thinkingConfig;\n if (tc && (tc.includeThoughts !== undefined || tc.thinkingBudget !== undefined)) {\n hasUpdates = true;\n updateMessage.session.generation_config = {\n ...updateMessage.session.generation_config,\n thinking_config: {\n ...(tc.includeThoughts !== undefined && { include_thoughts: tc.includeThoughts }),\n ...(tc.thinkingBudget !== undefined && { thinking_budget: tc.thinkingBudget }),\n },\n };\n\n this.options.thinkingConfig = tc;\n this.log('Updating thinkingConfig');\n }\n\n // Update instructions if provided\n if (config.instructions !== undefined) {\n hasUpdates = true;\n updateMessage.session.system_instruction = {\n parts: [{ text: config.instructions }],\n };\n\n this.log('Updating instructions');\n }\n\n // Mirror `sendInitialConfig`: flatten both tool sources — the explicit `config.tools` arg\n // and the `addTools()` registry — into a single `function_declarations` container so the\n // model receives every available tool. The multi-container shape previously emitted here\n // was accepted by Gemini at setup but suppressed tool_call frames mid-session, reintroducing #17018.\n const hasRegisteredTools = !!this.tools && Object.keys(this.tools).length > 0;\n // `config.tools: []` is the explicit-clear signal; honor it even when the `addTools()`\n // registry is non-empty, otherwise the caller has no way to remove all tools mid-session.\n const isExplicitClear = Array.isArray(config.tools) && config.tools.length === 0;\n if (config.tools !== undefined || hasRegisteredTools) {\n hasUpdates = true;\n const declarations = isExplicitClear ? [] : this.buildToolDeclarations(config.tools, this.tools);\n if (declarations.length > 0) {\n updateMessage.session.tools = [{ function_declarations: declarations }];\n } else {\n updateMessage.session.tools = [];\n }\n this.log('Updating tools:', declarations.length, 'tools');\n }\n\n // Update session configuration if provided\n if (config.sessionConfig) {\n // Handle VAD settings\n if (config.sessionConfig.vad) {\n hasUpdates = true;\n updateMessage.session.vad = {\n enabled: config.sessionConfig.vad.enabled ?? true,\n sensitivity: config.sessionConfig.vad.sensitivity ?? 0.5,\n silence_duration_ms: config.sessionConfig.vad.silenceDurationMs ?? 1000,\n };\n this.log('Updating VAD settings:', config.sessionConfig.vad);\n }\n\n // Handle interrupt settings\n if (config.sessionConfig.interrupts) {\n hasUpdates = true;\n updateMessage.session.interrupts = {\n enabled: config.sessionConfig.interrupts.enabled ?? true,\n allow_user_interruption: config.sessionConfig.interrupts.allowUserInterruption ?? true,\n };\n this.log('Updating interrupt settings:', config.sessionConfig.interrupts);\n }\n\n // Handle context compression\n if (config.sessionConfig.contextCompression !== undefined) {\n hasUpdates = true;\n updateMessage.session.context_compression = config.sessionConfig.contextCompression;\n this.log('Updating context compression:', config.sessionConfig.contextCompression);\n // Apply to ContextManager\n this.contextManager.setCompressionEnabled(config.sessionConfig.contextCompression);\n }\n }\n\n // Check if there are any updates to send\n if (!hasUpdates) {\n this.log('No valid configuration updates to send');\n resolve();\n return;\n }\n\n // Set up timeout for response\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Session configuration update timeout - no response received'));\n }, 10000); // 10 second timeout\n\n // Listen for update confirmation\n const onSessionUpdated = (data: GeminiLiveServerMessage) => {\n cleanup();\n this.log('Session configuration updated successfully', data);\n resolve();\n };\n\n // Listen for errors\n const onError = (error: { message?: string; code?: string; details?: unknown }) => {\n cleanup();\n this.log('Session configuration update failed', error);\n reject(new Error(`Failed to update session configuration: ${error.message || 'Unknown error'}`));\n };\n\n // Set up event listeners\n const cleanup = () => {\n clearTimeout(timeout);\n this.eventManager.getEventEmitter().removeListener('session.updated', onSessionUpdated as any);\n this.eventManager.getEventEmitter().removeListener('error', onError as any);\n };\n\n this.eventManager.getEventEmitter().once('session.updated', onSessionUpdated as any);\n this.eventManager.getEventEmitter().once('error', onError as any);\n\n // Send the update message\n try {\n this.sendEvent('session.update', updateMessage);\n this.log('Sent session configuration update', updateMessage);\n } catch (error) {\n cleanup();\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n this.log('Failed to send session configuration update', error);\n reject(new Error(`Failed to send session configuration update: ${errorMessage}`));\n }\n });\n }\n\n /**\n * Get current connection state\n */\n getConnectionState(): 'disconnected' | 'connected' {\n return this.state;\n }\n\n /**\n * Check if currently connected\n */\n isConnected(): boolean {\n return this.state === 'connected';\n }\n\n /**\n * Get current speaker stream for audio concatenation\n * This allows external access to the current audio stream being built\n */\n getCurrentSpeakerStream(): NodeJS.ReadableStream | null {\n return this.audioStreamManager.getCurrentSpeakerStream();\n }\n\n /**\n * Get session handle for resumption\n */\n getSessionHandle(): string | undefined {\n // TODO: Return actual session handle when Gemini Live API supports session resumption\n return this.sessionHandle;\n }\n\n /**\n * Get comprehensive session information\n */\n getSessionInfo(): {\n id?: string;\n handle?: string;\n startTime?: Date;\n duration?: number;\n state: string;\n config?: GeminiSessionConfig;\n contextSize: number;\n } {\n return {\n id: this.sessionId,\n handle: this.sessionHandle,\n startTime: this.sessionStartTime ? new Date(this.sessionStartTime) : undefined,\n duration: this.sessionStartTime ? Date.now() - this.sessionStartTime : undefined,\n state: this.state,\n config: this.options.sessionConfig,\n contextSize: this.contextManager.getContextSize(),\n };\n }\n\n /**\n * Get session context history\n */\n getContextHistory(): Array<{ role: string; content: string; timestamp: number }> {\n return this.contextManager.getContextHistory();\n }\n\n /**\n * Add to context history for session continuity\n */\n addToContext(role: 'user' | 'assistant', content: string): void {\n this.contextManager.addEntry(role, content);\n }\n\n /**\n * Clear session context\n */\n clearContext(): void {\n this.contextManager.clearContext();\n this.log('Session context cleared');\n }\n\n /**\n * Enable or disable automatic reconnection\n */\n setAutoReconnect(enabled: boolean): void {\n if (!this.options.sessionConfig) {\n this.options.sessionConfig = {};\n }\n this.options.sessionConfig.enableResumption = enabled;\n this.log(`Auto-reconnect ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /**\n * Send session resumption message\n * @private\n */\n /**\n * Start monitoring session duration\n * @private\n */\n private startSessionDurationMonitor(): void {\n if (!this.options.sessionConfig?.maxDuration) {\n return;\n }\n\n // Parse duration string (e.g., '24h', '2h', '30m')\n const durationMs = this.parseDuration(this.options.sessionConfig.maxDuration);\n\n if (!durationMs) {\n this.log('Invalid session duration format', { duration: this.options.sessionConfig.maxDuration });\n return;\n }\n\n // Clear existing monitor if any\n this.clearSessionTimers();\n\n // Set timeout for session expiry warning\n const warningTime = durationMs - 5 * 60 * 1000; // 5 minutes before expiry\n\n if (warningTime > 0) {\n this.sessionExpiringTimeout = setTimeout(() => {\n this.emit('sessionExpiring', {\n expiresIn: 5 * 60 * 1000,\n sessionId: this.sessionId,\n });\n }, warningTime);\n }\n\n // Set timeout for session expiry\n this.sessionDurationTimeout = setTimeout(() => {\n this.log('Session duration limit reached, disconnecting');\n void this.disconnect();\n }, durationMs);\n }\n\n /**\n * Clear the session expiry and expiry-warning timers\n * @private\n */\n private clearSessionTimers(): void {\n if (this.sessionDurationTimeout) {\n clearTimeout(this.sessionDurationTimeout);\n this.sessionDurationTimeout = undefined;\n }\n if (this.sessionExpiringTimeout) {\n clearTimeout(this.sessionExpiringTimeout);\n this.sessionExpiringTimeout = undefined;\n }\n }\n\n /**\n * Release per-session resources when the socket goes away without disconnect().\n * disconnect() returns early once state is 'disconnected', so the socket close and\n * error handlers have to end speaker streams and clear session timers themselves.\n * @private\n */\n private releaseSessionResources(): void {\n this.clearSessionTimers();\n this.audioStreamManager.cleanupSpeakerStreams();\n }\n\n /**\n * Parse duration string to milliseconds\n * @private\n */\n private parseDuration(duration: string): number | null {\n const match = duration.match(/^(\\d+)([hms])$/);\n if (!match) return null;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2];\n\n switch (unit) {\n case 'h':\n return value * 60 * 60 * 1000;\n case 'm':\n return value * 60 * 1000;\n case 's':\n return value * 1000;\n default:\n return null;\n }\n }\n\n /**\n * Compress context history to manage memory\n * @private\n */\n private compressContext(): void {\n // Deprecated: ContextManager handles compression\n this.log('compressContext is deprecated; handled by ContextManager');\n }\n\n /**\n * Setup WebSocket event listeners for Gemini Live API messages\n * @private\n */\n private setupEventListeners(): void {\n if (!this.ws) {\n throw new Error('WebSocket not initialized');\n }\n\n // Handle WebSocket connection events\n this.ws.on('open', () => {\n this.log('WebSocket connection opened');\n // Note: We transition to 'connected' in connect() after setup is complete\n // This is just confirming the WebSocket is open\n });\n\n this.ws.on('close', (code: number, reason: Buffer) => {\n const reasonText = reason.toString();\n this.log('WebSocket connection closed', { code, reason: reasonText });\n this.state = 'disconnected';\n this.releaseSessionResources();\n this.emit('session', { state: 'disconnected', code, reason: reasonText });\n\n // A clean server-initiated close (e.g. 1007 invalid-argument for a bad model id or a\n // malformed setup frame) arrives as `close`, not as a socket `error`. Surface it as an\n // `error` so a pending connect() (waitForSessionCreated) rejects immediately with the\n // real close code and reason instead of waiting out the 30s setup timeout.\n if (code !== 1000) {\n this.emit('error', {\n message: `WebSocket closed during/after setup (code ${code})${reasonText ? `: ${reasonText}` : ''}`,\n code: 'websocket_closed',\n details: { code, reason: reasonText },\n });\n }\n });\n\n this.ws.on('error', (error: Error) => {\n this.log('WebSocket error', error);\n this.state = 'disconnected';\n this.releaseSessionResources();\n this.emit('session', { state: 'disconnected' });\n this.emit('error', {\n message: error.message,\n code: 'websocket_error',\n details: error,\n });\n });\n\n // Handle incoming messages from Gemini Live API\n this.ws.on('message', async (message: Buffer | string) => {\n try {\n const data = JSON.parse(message.toString());\n await this.handleGeminiMessage(data);\n } catch (error) {\n this.log('Failed to parse WebSocket message', error);\n this.emit('error', {\n message: 'Failed to parse WebSocket message',\n code: 'parse_error',\n details: error,\n });\n }\n });\n }\n\n /**\n * Handle different types of messages from Gemini Live API\n * @private\n */\n private async handleGeminiMessage(data: GeminiLiveServerMessage): Promise<void> {\n // Always log received messages in debug mode or when troubleshooting\n this.log('Received message:', JSON.stringify(data, null, 2));\n\n // Extract response ID if present in the message\n if ((data as any).responseId) {\n this.setCurrentResponseId((data as any).responseId);\n this.log('Set current response ID:', (data as any).responseId);\n }\n\n // Handle different Gemini Live API message structures\n if (data.setup) {\n this.log('Processing setup message');\n this.handleSetupComplete(data);\n } else if (data.setupComplete) {\n this.log('Processing setupComplete message');\n this.handleSetupComplete(data);\n } else if (data.serverContent) {\n this.log('Processing server content message');\n this.handleServerContent(data.serverContent);\n } else if (data.toolCall) {\n this.log('Processing tool call message');\n await this.handleToolCall(data);\n }\n\n // Usage metadata and session resumption updates can accompany any primary message.\n // Handle them independently so content, setup, and tool frames do not suppress them.\n if (data.usageMetadata) {\n this.log('Processing usage metadata message');\n this.handleUsageUpdate(data);\n }\n if (data.sessionResumptionUpdate) {\n this.log('Processing session resumption update', data.sessionResumptionUpdate);\n if (data.sessionResumptionUpdate.resumable && data.sessionResumptionUpdate.newHandle) {\n this.sessionHandle = data.sessionResumptionUpdate.newHandle;\n this.log('Session handle updated from server', { handle: this.sessionHandle });\n this.emit('sessionHandle', {\n handle: this.sessionHandle,\n expiresAt: new Date(Date.now() + 2 * 60 * 60 * 1000), // 2h TTL for AI Studio\n });\n }\n } else if (data.sessionEnd) {\n this.log('Processing session end message');\n this.handleSessionEnd(data);\n } else if (data.error) {\n this.log('Processing error message');\n this.handleError(data.error);\n } else {\n // Handle alternative message formats by checking for common fields\n const messageData = data as any; // Use any for flexible message handling\n\n // Check for various possible setup completion indicators\n if (messageData.type === 'setup' || messageData.type === 'session.ready' || messageData.type === 'ready') {\n // Handle alternative setup message formats\n this.log('Processing alternative setup message with type:', messageData.type);\n this.handleSetupComplete(data);\n } else if (messageData.sessionHandle) {\n // Handle session handle in response\n this.log('Processing session handle message');\n this.handleSetupComplete(data);\n } else if (\n messageData.session ||\n messageData.ready ||\n messageData.status === 'ready' ||\n messageData.status === 'setup_complete'\n ) {\n // Try to handle as setup completion if it has any setup-related fields\n this.log('Processing setup completion message with status:', messageData.status);\n this.handleSetupComplete(data);\n } else if (messageData.candidates || messageData.promptFeedback) {\n // Handle successful response from BidiGenerateContent\n this.log('Processing BidiGenerateContent response');\n this.handleSetupComplete(data);\n } else if (messageData.contents && Array.isArray(messageData.contents)) {\n // Handle content response\n this.log('Processing content response');\n this.handleServerContent({ modelTurn: { parts: messageData.contents.flatMap((c: any) => c.parts || []) } });\n // Also treat this as setup completion since we got a response\n this.handleSetupComplete(data);\n } else if (messageData.candidates && Array.isArray(messageData.candidates)) {\n // Handle candidates response (common in Gemini API)\n this.log('Processing candidates response');\n this.handleSetupComplete(data);\n } else {\n this.log('Unknown message format - no recognized fields found');\n }\n }\n }\n\n /**\n * Handle setup completion message\n * @private\n */\n private handleSetupComplete(data: GeminiLiveServerMessage): void {\n this.log('Setup completed');\n\n // Process all queued messages now that the session is ready\n const queue = this.queue.splice(0, this.queue.length);\n if (queue.length > 0) {\n this.log('Processing queued messages:', queue.length);\n for (const queuedMessage of queue) {\n try {\n this.connectionManager.send(JSON.stringify(queuedMessage));\n this.log('Sent queued message:', queuedMessage);\n } catch (err) {\n this.log('Failed to send queued message, re-queuing:', err);\n this.queue.unshift(queuedMessage);\n break;\n }\n }\n }\n\n // Emit event for waitForSessionCreated to resolve\n this.eventManager.getEventEmitter().emit('setupComplete', data as any);\n // Session is now ready for communication\n }\n\n /**\n * Handle session update confirmation\n * @private\n */\n private handleSessionUpdated(data: GeminiLiveServerMessage): void {\n this.log('Session updated', data);\n // Emit event for updateSessionConfig to resolve\n this.eventManager.getEventEmitter().emit('session.updated', data as any);\n\n // Also emit a general session event for any external listeners\n this.emit('session', {\n state: 'updated',\n config: data as Record<string, unknown>,\n });\n }\n\n /**\n * Handle server content (text/audio responses)\n * @private\n */\n private handleServerContent(data: GeminiLiveServerMessage['serverContent']): void {\n if (!data) {\n return;\n }\n\n // Barge-in: the server cancelled the in-flight model response because the\n // user started speaking. Surface this as the `interrupt` event so consumers\n // can drop queued TTS audio. Matches the `interrupt` shape emitted by\n // `@mastra/voice-aws-nova-sonic`.\n //\n // The cancelled turn will not necessarily be followed by `turnComplete`, so\n // end any in-flight speaker streams here. Otherwise stream counters never\n // decrement and downstream playback hangs on the cancelled audio. Discard\n // the partial assistant text — it does not represent a completed turn.\n if (data.interrupted) {\n this.log('Model response interrupted by user activity');\n this.audioStreamManager.cleanupSpeakerStreams();\n this.pendingAssistantResponse = '';\n this.emit('interrupt', { type: 'user', timestamp: Date.now() });\n }\n\n // User-side transcription. Emitted as `writing` with `role: 'user'`,\n // matching the OpenAI / xAI / Inworld realtime pattern of using a single\n // `writing` channel with role disambiguation.\n if (data.inputTranscription?.text) {\n this.emit('writing', {\n text: data.inputTranscription.text,\n role: 'user',\n });\n }\n\n // Model-side transcription. On native-audio models this is the\n // authoritative source for the spoken response — emit it as `writing` with\n // `role: 'assistant'`. On non-native-audio models this field is not sent\n // (the spoken response comes from `modelTurn.parts.text` below).\n if (data.outputTranscription?.text) {\n this.pendingAssistantResponse += data.outputTranscription.text;\n this.emit('writing', {\n text: data.outputTranscription.text,\n role: 'assistant',\n });\n }\n\n const nativeAudio = this.isNativeAudioModel();\n\n if (data.modelTurn?.parts) {\n for (const part of data.modelTurn.parts) {\n // Handle text content. Native-audio models put their internal reasoning\n // here while the spoken response goes through `outputTranscription`\n // above — route reasoning to the Gemini-specific `thinking` event so\n // consumers can render it separately without conflating it with the\n // assistant's actual reply. Non-native-audio models do not send\n // `outputTranscription`, so `part.text` IS the spoken response and\n // continues to flow through `writing`.\n if (part.text) {\n if (nativeAudio) {\n this.emit('thinking', { text: part.text });\n } else {\n this.pendingAssistantResponse += part.text;\n this.emit('writing', {\n text: part.text,\n role: 'assistant',\n });\n }\n }\n\n // Handle function calls (tool calls) embedded in parts\n // Gemini Live API sends tool calls inside serverContent.modelTurn.parts\n if (part.functionCall) {\n this.log('Found function call in serverContent.modelTurn.parts', part.functionCall);\n\n // Convert to toolCall format and handle\n const toolCallData: GeminiLiveServerMessage = {\n toolCall: {\n name: part.functionCall.name,\n args: part.functionCall.args || {},\n id: part.functionCall.id || globalThis.crypto.randomUUID(),\n },\n };\n\n // Handle the tool call asynchronously\n void this.handleToolCall(toolCallData);\n\n // Continue to next part without processing audio/text\n continue;\n }\n\n // Handle audio content - implement chunk concatenation with proper response ID tracking\n if (part.inlineData?.mimeType?.includes('audio') && typeof part.inlineData.data === 'string') {\n try {\n const audioData = part.inlineData.data;\n const int16Array = this.audioStreamManager.base64ToInt16Array(audioData);\n\n // Use the tracked response ID or generate one if not available\n const responseId = this.getCurrentResponseId() || globalThis.crypto.randomUUID();\n\n // Get or create the speaker stream for this response\n let speakerStream = this.audioStreamManager.getSpeakerStream(responseId);\n if (!speakerStream) {\n // Clean up stale streams and enforce limits before creating new ones\n this.audioStreamManager.cleanupStaleStreams();\n this.audioStreamManager.enforceStreamLimits();\n\n // Create new stream through the manager\n speakerStream = this.audioStreamManager.createSpeakerStream(responseId);\n\n // Add error handling to the stream\n speakerStream.on('error', (streamError: Error) => {\n this.log(`Speaker stream error for ${responseId}:`, streamError);\n this.audioStreamManager.removeSpeakerStream(responseId);\n this.emit('error', {\n message: 'Speaker stream error',\n code: 'speaker_stream_error',\n details: { responseId, error: streamError },\n });\n });\n\n // Auto-cleanup when stream ends\n speakerStream.on('end', () => {\n this.log(`Speaker stream ended for response: ${responseId}`);\n this.audioStreamManager.removeSpeakerStream(responseId);\n });\n\n // Auto-cleanup when stream is destroyed\n speakerStream.on('close', () => {\n this.log(`Speaker stream closed for response: ${responseId}`);\n this.audioStreamManager.removeSpeakerStream(responseId);\n });\n\n this.log('Created new speaker stream for response:', responseId);\n\n // Emit the speaker stream for external listeners\n this.emit('speaker', speakerStream as NodeJS.ReadableStream);\n }\n\n // Write the audio chunk to the stream\n const audioBuffer = Buffer.from(int16Array.buffer, int16Array.byteOffset, int16Array.byteLength);\n speakerStream.write(audioBuffer);\n\n this.log('Wrote audio chunk to stream:', {\n responseId,\n chunkSize: audioBuffer.length,\n totalStreams: this.audioStreamManager.getActiveStreamCount(),\n });\n\n // Also emit the individual speaking event for backward compatibility\n this.emit('speaking', {\n audio: audioData, // Base64 string\n audioData: int16Array,\n sampleRate: this.audioConfig.outputSampleRate, // Gemini Live outputs at 24kHz\n });\n } catch (error) {\n this.log('Error processing audio data:', error);\n this.emit('error', {\n message: 'Failed to process audio data',\n code: 'audio_processing_error',\n details: error,\n });\n }\n }\n }\n }\n\n // Check for turn completion\n if (data.turnComplete) {\n this.log('Turn completed');\n\n // Flush the assistant text accumulated across this turn's `serverContent`\n // frames as a single context entry. Doing this once per turn (rather than\n // once per frame) keeps the conversation history coherent even when the\n // Live API streams a response over many incremental frames.\n if (this.pendingAssistantResponse.trim()) {\n this.addToContext('assistant', this.pendingAssistantResponse);\n }\n this.pendingAssistantResponse = '';\n\n // End all active speaker streams for this turn\n this.audioStreamManager.cleanupSpeakerStreams();\n\n // Emit turn completion event\n this.emit('turnComplete', {\n timestamp: Date.now(),\n });\n }\n }\n\n /**\n * Handle tool call requests from the model\n * @private\n */\n private async handleToolCall(data: GeminiLiveServerMessage): Promise<void> {\n if (!data.toolCall) {\n return;\n }\n\n // Handle both formats:\n // 1. Direct format: { toolCall: { name, args, id } }\n // 2. Array format: { toolCall: { functionCalls: [{ name, args, id }] } }\n let toolCalls: Array<{ name?: string; args?: Record<string, any>; id?: string }> = [];\n\n if (data.toolCall.functionCalls && Array.isArray(data.toolCall.functionCalls)) {\n // Array format (actual Gemini API format)\n toolCalls = data.toolCall.functionCalls;\n } else if (data.toolCall.name) {\n // Direct format (for backward compatibility)\n toolCalls = [{ name: data.toolCall.name, args: data.toolCall.args, id: data.toolCall.id }];\n }\n\n // Process each tool call\n for (const toolCall of toolCalls) {\n const toolName = toolCall.name || '';\n const toolArgs = toolCall.args || {};\n const toolId = toolCall.id || globalThis.crypto.randomUUID();\n\n await this.processSingleToolCall(toolName, toolArgs, toolId);\n }\n }\n\n /**\n * Process a single tool call\n * @private\n */\n private async processSingleToolCall(toolName: string, toolArgs: Record<string, any>, toolId: string): Promise<void> {\n this.log('Processing tool call', { toolName, toolArgs, toolId });\n\n // Skip duplicates: the same call id may arrive through both inbound\n // representations (top-level `toolCall` and\n // `serverContent.modelTurn.parts[].functionCall`).\n if (this.processedToolCallIds.has(toolId)) {\n this.log('Duplicate tool call ignored', { toolName, toolId });\n return;\n }\n this.processedToolCallIds.add(toolId);\n if (this.processedToolCallIds.size > MAX_TRACKED_TOOL_CALL_IDS) {\n const oldest = this.processedToolCallIds.values().next().value;\n if (oldest !== undefined) {\n this.processedToolCallIds.delete(oldest);\n }\n }\n\n // Emit tool call event\n this.emit('toolCall', {\n name: toolName,\n args: toolArgs,\n id: toolId,\n });\n\n // Find the tool\n const tool = this.tools?.[toolName];\n if (!tool) {\n this.log('Tool not found', { toolName });\n\n // Gemini blocks the turn until every `functionCall` id in the batch is answered. Returning\n // without a `functionResponse` leaves the call unanswered forever, so the model never speaks\n // again (dead air until hangup). Send an error response for this id before emitting.\n const notFoundMessage = {\n toolResponse: {\n functionResponses: [\n {\n id: toolId,\n name: toolName,\n response: { error: `Tool \"${toolName}\" not found` },\n },\n ],\n },\n };\n\n this.sendEvent('toolResponse', notFoundMessage);\n\n this.createAndEmitError(GeminiLiveErrorCode.TOOL_NOT_FOUND, `Tool \"${toolName}\" not found`, {\n toolName,\n availableTools: Object.keys(this.tools || {}),\n });\n return;\n }\n\n try {\n // Execute the tool\n let result: unknown;\n\n if (tool.execute) {\n this.log('Executing tool', { toolName, toolArgs });\n\n // Execute with proper context\n result = await tool.execute(toolArgs, { requestContext: this.requestContext });\n\n this.log('Tool executed successfully', { toolName, result });\n } else {\n this.log('Tool has no execute function', { toolName });\n result = { error: 'Tool has no execute function' };\n }\n\n // Gemini Live's `response` proto field is a struct, not repeating. Tools returning arrays\n // or primitives close the session with `1007 Unknown name \"response\": Proto field is not\n // repeating`. Wrap everything except plain objects in `{ result }` so the field is always a\n // struct — `Date`, `Map`, `Set`, `Error`, and class instances all serialize as `{}` if sent\n // bare (their own enumerable properties are empty), losing the tool's data silently.\n const responsePayload = isPlainObject(result) ? result : { result };\n\n // Send tool result back to Gemini Live API\n const toolResultMessage = {\n toolResponse: {\n functionResponses: [\n {\n id: toolId,\n name: toolName,\n response: responsePayload,\n },\n ],\n },\n };\n\n this.sendEvent('toolResponse', toolResultMessage);\n this.log('Tool result sent', { toolName, toolId, result });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n this.log('Tool execution failed', { toolName, error: errorMessage });\n\n // Send error result back to Gemini Live API\n const errorResultMessage = {\n toolResponse: {\n functionResponses: [\n {\n id: toolId,\n name: toolName,\n response: { error: errorMessage },\n },\n ],\n },\n };\n\n this.sendEvent('toolResponse', errorResultMessage);\n\n // Emit error event\n this.createAndEmitError(GeminiLiveErrorCode.TOOL_EXECUTION_ERROR, `Tool execution failed: ${errorMessage}`, {\n toolName,\n toolArgs,\n error,\n });\n }\n }\n\n /**\n * Handle token usage information\n * @private\n */\n private handleUsageUpdate(data: GeminiLiveServerMessage): void {\n if (data.usageMetadata) {\n this.emit('usage', {\n inputTokens: data.usageMetadata.promptTokenCount || 0,\n outputTokens: data.usageMetadata.responseTokenCount || 0,\n totalTokens: data.usageMetadata.totalTokenCount || 0,\n modality: this.determineModality(data),\n });\n }\n }\n\n /**\n * Handle session end\n * @private\n */\n private handleSessionEnd(data: GeminiLiveServerMessage): void {\n this.log('Session ended', data.sessionEnd?.reason);\n this.state = 'disconnected';\n this.emit('session', { state: 'disconnected' });\n }\n\n /**\n * Handle errors\n * @private\n */\n private handleError(error: GeminiLiveServerMessage['error']): void {\n if (!error) {\n this.log('Received error from Gemini Live API (no error details)');\n return;\n }\n\n this.log('Received error from Gemini Live API', error);\n this.emit('error', {\n message: error.message || 'Unknown error',\n code: error.code || 'unknown_error',\n details: error.details,\n });\n }\n\n /**\n * Determine the modality from message data\n * @private\n */\n private determineModality(data: GeminiLiveServerMessage): 'audio' | 'text' | 'video' {\n // Simple heuristic - this could be more sophisticated\n if (data.serverContent?.modelTurn?.parts?.some(part => part.inlineData?.mimeType?.includes('audio'))) {\n return 'audio';\n }\n // Support for video is not yet implemented, leaving this here for future use if needed\n if (data.serverContent?.modelTurn?.parts?.some(part => part.inlineData?.mimeType?.includes('video'))) {\n return 'video';\n }\n return 'text';\n }\n\n /**\n * Resolve Vertex AI location with sensible default\n * @private\n */\n private getVertexLocation(): string {\n return this.options.location?.trim() || 'us-central1';\n }\n\n /**\n * Whether the active model is a Gemini Live \"native-audio\" variant.\n *\n * Native-audio models emit a different `serverContent.modelTurn.parts.text`\n * stream than their half-cascade siblings: on native-audio, that text is the\n * model's internal reasoning (chain-of-thought), and the *spoken* response\n * arrives separately via `serverContent.outputTranscription.text`. On\n * non-native-audio models there is no `outputTranscription` channel, and\n * `modelTurn.parts.text` is the spoken response.\n *\n * Used to decide whether `modelTurn.parts.text` should be emitted as\n * `thinking` (native-audio) or `writing` (non-native-audio). All native-audio\n * model IDs in `GeminiVoiceModel` contain the literal substring\n * `native-audio`, so a substring check is sufficient and forward-compatible\n * with new variants that follow the same naming convention.\n * @private\n */\n private isNativeAudioModel(): boolean {\n const model = this.options.model ?? DEFAULT_MODEL;\n return model.includes('native-audio');\n }\n\n /**\n * Resolve the correct model identifier for Gemini API or Vertex AI\n * @private\n */\n private resolveModelIdentifier(): string {\n const model = this.options.model ?? DEFAULT_MODEL;\n\n if (!this.options.vertexAI) {\n return `models/${model}`;\n }\n\n if (!this.options.project) {\n throw this.createAndEmitError(\n GeminiLiveErrorCode.PROJECT_ID_MISSING,\n 'Google Cloud project ID is required when using Vertex AI.',\n );\n }\n\n const location = this.getVertexLocation();\n return `projects/${this.options.project}/locations/${location}/publishers/google/models/${model}`;\n }\n\n /**\n * Send initial configuration to Gemini Live API\n * @private\n */\n private sendInitialConfig(): void {\n if (!this.ws || !this.connectionManager.isConnected()) {\n throw new Error('WebSocket not connected');\n }\n\n // Live API setup message. Keys must be snake_case to match Gemini Live's wire format —\n // camelCase keys cause native-audio models to reject the setup with\n // `1007 Cannot extract voices from a non-audio request`. Matches the `UpdateMessage` shape\n // already used by this package's `session.update` path.\n interface LiveGenerateContentSetup {\n model?: string;\n generation_config?: {\n temperature?: number;\n top_k?: number;\n top_p?: number;\n max_output_tokens?: number;\n stop_sequences?: string[];\n candidate_count?: number;\n response_modalities?: ('AUDIO' | 'TEXT')[];\n speech_config?: {\n voice_config?: {\n prebuilt_voice_config?: {\n voice_name?: string;\n };\n };\n };\n thinking_config?: {\n include_thoughts?: boolean;\n thinking_budget?: number;\n };\n };\n system_instruction?: {\n parts: Array<{\n text: string;\n }>;\n };\n tools?: Array<{\n function_declarations: Array<{\n name: string;\n description?: string;\n parameters?: unknown;\n }>;\n }>;\n /**\n * Empty-object flag that enables server-side ASR for the user's spoken\n * input. When set, the server emits `serverContent.inputTranscription.text`\n * frames alongside audio. Required to surface what the user said.\n */\n input_audio_transcription?: Record<string, never>;\n /**\n * Empty-object flag that enables server-side transcription of the model's\n * spoken output. When set, the server emits\n * `serverContent.outputTranscription.text` frames. Required on\n * native-audio models — without this, the spoken response text is never\n * delivered to the client (`modelTurn.parts.text` on native-audio is\n * reasoning, not spoken content).\n */\n output_audio_transcription?: Record<string, never>;\n /**\n * Activity handling tells the server how to react to new user activity\n * while the model is still responding. `START_OF_ACTIVITY_INTERRUPTS`\n * cancels the in-flight model response on barge-in and sets\n * `serverContent.interrupted = true`, which is the signal consumers need\n * to drop queued TTS audio.\n */\n realtime_input_config?: {\n activity_handling?: 'START_OF_ACTIVITY_INTERRUPTS' | 'NO_INTERRUPTION';\n };\n /**\n * Session resumption config. Empty object enables server-issued tokens;\n * { handle } resumes a previous session.\n */\n session_resumption?: { handle?: string };\n /**\n * Opts the session into seeding initial conversation history via send_client_content frames.\n * Must be present in the setup frame for sendContext() to work on gemini-3.1-flash-live-preview\n * and later 3.x models — without it the server closes the WebSocket with code 1007.\n */\n history_config?: {\n initial_history_in_client_content?: boolean;\n };\n }\n\n // Native-audio models require `response_modalities: [\"AUDIO\"]` at setup time. This is a voice\n // library, so AUDIO is the only sensible session-level default; callers needing a TEXT-only\n // turn can override per-turn via `GeminiLiveVoiceOptions.responseModalities` on `speak()`.\n const generationConfig: NonNullable<LiveGenerateContentSetup['generation_config']> = {\n response_modalities: ['AUDIO'],\n };\n\n // Only attach `voice_config` when the caller supplied a `speaker`. Omitting the field lets\n // Gemini Live pick its server-side default and avoids pinning a Mastra-side preference.\n if (this.options.speaker) {\n generationConfig.speech_config = {\n voice_config: {\n prebuilt_voice_config: {\n voice_name: this.options.speaker,\n },\n },\n };\n }\n\n // Forward caller-supplied thinking configuration. Public config is camelCase; the wire\n // format is snake_case (`thinking_config.include_thoughts` / `thinking_budget`), matching\n // the translation done for `speech_config` above. Only emit `thinking_config` when at least\n // one sub-field is set, so an empty `thinkingConfig: {}` is a no-op and the setup frame stays\n // byte-for-byte unchanged — a bare `thinking_config: {}` may be rejected by non-thinking models.\n const setupThinking = this.options.thinkingConfig;\n if (setupThinking && (setupThinking.includeThoughts !== undefined || setupThinking.thinkingBudget !== undefined)) {\n generationConfig.thinking_config = {\n ...(setupThinking.includeThoughts !== undefined && { include_thoughts: setupThinking.includeThoughts }),\n ...(setupThinking.thinkingBudget !== undefined && { thinking_budget: setupThinking.thinkingBudget }),\n };\n }\n\n // Build the Live API setup message\n const setupMessage: { setup: LiveGenerateContentSetup } = {\n setup: {\n model: this.resolveModelIdentifier(),\n generation_config: generationConfig,\n // Transcription is on by default — matches the pattern in @mastra/voice-openai-realtime,\n // @mastra/voice-xai-realtime, and @mastra/voice-inworld where realtime sessions\n // unconditionally enable STT in `connect()`. On native-audio models this is the ONLY way\n // to receive the spoken response as text (`modelTurn.parts.text` carries reasoning, not\n // speech), so without these flags the assistant's words are silently dropped client-side.\n input_audio_transcription: {},\n output_audio_transcription: {},\n // Activity-based interrupts surface barge-in as `serverContent.interrupted = true` and\n // cancel the in-flight model response. This is the only way to wire up the `interrupt`\n // event declared in `GeminiLiveEventMap`.\n realtime_input_config: {\n activity_handling: 'START_OF_ACTIVITY_INTERRUPTS',\n },\n // Session resumption: empty object requests server-issued tokens on new sessions;\n // { handle } resumes a previous session. Only included when enableResumption is set.\n ...(this.options.sessionConfig?.enableResumption && {\n session_resumption: this.isResuming && this.sessionHandle ? { handle: this.sessionHandle } : {},\n }),\n // history_config opts the session into send_client_content-based history seeding.\n // Required by gemini-3.1-flash-live-preview (and later 3.x models) for sendContext() to\n // succeed — without this field in the setup frame the server closes the WebSocket with\n // code 1007 (\"Request contains an invalid argument.\"). Defaults to true so that\n // sendContext() works out of the box; set initialHistoryInClientContent: false on\n // sessionConfig to opt out explicitly.\n ...(this.options.sessionConfig?.initialHistoryInClientContent !== false && {\n history_config: {\n initial_history_in_client_content: true,\n },\n }),\n },\n };\n\n // Add system instructions if provided\n if (this.options.instructions) {\n setupMessage.setup.system_instruction = {\n parts: [{ text: this.options.instructions }],\n };\n }\n\n // Gemini Live expects a single `tools` entry whose `function_declarations` array holds every\n // tool. The previous shape (one entry per tool, camelCase `functionDeclarations`) was accepted\n // at setup but the model never emitted tool_call frames back to the client.\n const functionDeclarations = this.buildToolDeclarations(this.options.tools, this.tools);\n\n // Emit tools as a single container with all declarations\n if (functionDeclarations.length > 0) {\n setupMessage.setup.tools = [{ function_declarations: functionDeclarations }];\n this.log('Including tools in setup message', { toolCount: functionDeclarations.length });\n }\n\n this.log('Sending Live API setup message:', setupMessage);\n\n try {\n this.sendEvent('setup', setupMessage);\n } catch (error) {\n this.log('Failed to send Live API setup message:', error);\n throw new Error(\n `Failed to send Live API setup message: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Wait for Gemini Live session to be created and ready\n * @private\n */\n private waitForSessionCreated(): Promise<void> {\n return new Promise((resolve, reject) => {\n // For Gemini Live API, we need to wait for the setup completion\n // This will be triggered by the setupComplete message type\n\n let isResolved = false;\n\n const onSetupComplete = () => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n resolve();\n }\n };\n\n const onError = (errorData: { message?: string; code?: string; details?: unknown }) => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(new Error(`Session creation failed: ${errorData.message || 'Unknown error'}`));\n }\n };\n\n const onSessionEnd = () => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(new Error('Session ended before setup completed'));\n }\n };\n\n const cleanup = () => {\n this.eventManager.getEventEmitter().removeListener('setupComplete', onSetupComplete as any);\n this.eventManager.getEventEmitter().removeListener('error', onError as any);\n this.eventManager.getEventEmitter().removeListener('sessionEnd', onSessionEnd as any);\n };\n\n // Listen for setup completion\n this.eventManager.getEventEmitter().once('setupComplete', onSetupComplete as any);\n this.eventManager.getEventEmitter().once('error', onError as any);\n this.eventManager.getEventEmitter().once('sessionEnd', onSessionEnd as any);\n\n // Add timeout to prevent hanging indefinitely\n setTimeout(() => {\n if (!isResolved) {\n isResolved = true;\n cleanup();\n reject(new Error('Session creation timeout'));\n }\n }, 30000); // 30 second timeout\n });\n }\n\n /**\n * Get OAuth access token for Vertex AI authentication\n * Implements token caching and automatic refresh\n * @private\n */\n private async getAccessToken(): Promise<string> {\n if (!this.options.vertexAI) {\n throw new Error('getAccessToken should only be called for Vertex AI mode');\n }\n return this.authManager.getAccessToken();\n }\n\n /**\n * Get the current response ID from the server message\n * This is needed to associate audio chunks with their respective responses.\n * @private\n */\n private getCurrentResponseId(): string | undefined {\n return this.audioStreamManager.getCurrentResponseId();\n }\n\n /**\n * Set the current response ID for the next audio chunk.\n * This is used to track the response ID for the current turn.\n * @private\n */\n private setCurrentResponseId(responseId: string): void {\n this.audioStreamManager.setCurrentResponseId(responseId);\n }\n\n /**\n * Send an event to the Gemini Live API with queueing support\n * @private\n */\n private sendEvent(type: string, data: any): void {\n // Handle messages that already have their own structure\n let message: any;\n if (type === 'setup' && data.setup) {\n // For setup messages, use the data as-is\n message = data;\n } else if (type === 'client_content' && data.client_content) {\n // For client_content messages, use the data as-is\n message = data;\n } else if (type === 'realtime_input' && data.realtime_input) {\n // For realtime_input messages, use the data as-is\n message = data;\n } else if (type === 'toolResponse' && data.toolResponse) {\n // For toolResponse messages, use the data as-is\n message = data;\n } else if (type === 'session.update' && data.session) {\n // For session update messages, use the data as-is\n message = data;\n } else {\n // For other messages, create the standard structure\n message = { type: type, ...data };\n }\n\n if (!this.ws || !this.connectionManager.isConnected()) {\n // Queue the message if WebSocket is not ready\n this.queue.push(message);\n this.log('Queued message:', { type, data });\n } else {\n // Send immediately if WebSocket is ready\n this.connectionManager.send(JSON.stringify(message));\n this.log('Sent message:', { type, data });\n }\n }\n\n /**\n * Equip the voice provider with tools\n * @param tools Object containing tool definitions that can be called by the voice model\n *\n * @example\n * ```typescript\n * const weatherTool = createTool({\n * id: \"getWeather\",\n * description: \"Get the current weather for a location\",\n * inputSchema: z.object({\n * location: z.string().describe(\"The city and state, e.g. San Francisco, CA\"),\n * }),\n * execute: async (inputData) => {\n * // Fetch weather data from an API\n * const response = await fetch(\n * `https://api.weather.com?location=${encodeURIComponent(inputData.location)}`,\n * );\n * const data = await response.json();\n * return {\n * message: `The current temperature in ${inputData.location} is ${data.temperature}°F with ${data.conditions}.`,\n * };\n * },\n * });\n *\n * voice.addTools({\n * getWeather: weatherTool,\n * });\n * ```\n */\n addTools(tools: ToolsInput): void {\n this.tools = tools;\n this.log('Tools added to Gemini Live Voice', { toolCount: Object.keys(tools || {}).length });\n }\n\n /**\n * Get the current tools configured for this voice instance\n * @returns Object containing the current tools\n */\n listTools(): ToolsInput | undefined {\n return this.tools;\n }\n\n private log(message: string, ...args: unknown[]): void {\n if (this.debug) {\n console.info(`[GeminiLiveVoice] ${message}`, ...args);\n }\n }\n\n /**\n * Flatten both tool sources (constructor `tools` option and runtime `addTools()` registry) into\n * the single declaration array Gemini Live expects inside `tools[0].function_declarations`.\n * Used by both `sendInitialConfig()` and `updateSessionConfig()` so mid-session tool updates\n * stay on the same shape as setup-time tools.\n * @private\n */\n private buildToolDeclarations(\n configTools: GeminiToolConfig[] | undefined,\n registeredTools: ToolsInput | undefined,\n ): Array<{ name: string; description?: string; parameters?: unknown }> {\n const declarations: Array<{ name: string; description?: string; parameters?: unknown }> = [];\n\n if (configTools && configTools.length > 0) {\n for (const tool of configTools) {\n declarations.push({\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n });\n }\n }\n\n if (registeredTools && Object.keys(registeredTools).length > 0) {\n for (const [toolName, tool] of Object.entries(registeredTools)) {\n try {\n let parameters: unknown;\n if ('inputSchema' in tool && tool.inputSchema) {\n if (typeof tool.inputSchema === 'object' && 'safeParse' in tool.inputSchema) {\n parameters = this.convertZodSchemaToJsonSchema(tool.inputSchema);\n } else {\n parameters = tool.inputSchema;\n }\n } else if ('parameters' in tool && tool.parameters) {\n parameters = tool.parameters;\n } else {\n parameters = { type: 'object', properties: {} };\n }\n\n declarations.push({\n name: toolName,\n description: tool.description || `Tool: ${toolName}`,\n parameters,\n });\n } catch (error) {\n this.log('Failed to process tool', { toolName, error });\n }\n }\n }\n\n return declarations;\n }\n\n /**\n * Convert a schema to Gemini-compatible JSON Schema using GoogleSchemaCompatLayer.\n * This ensures the output conforms to the OpenAPI 3.0 Schema Object subset\n * that Gemini's wire validator expects.\n * @private\n */\n private convertZodSchemaToJsonSchema(schema: unknown): unknown {\n try {\n const compat = new GoogleSchemaCompatLayer({\n provider: 'google',\n modelId: 'gemini-live',\n supportsStructuredOutputs: false,\n });\n return compat.processToJSONSchema(schema as any);\n } catch (error) {\n this.log('Failed to convert Zod schema to JSON schema', { error, schema });\n return {\n type: 'object',\n properties: {},\n description: 'Schema conversion failed',\n };\n }\n }\n\n /**\n * Close the connection (alias for disconnect)\n */\n close(): void {\n void this.disconnect();\n }\n\n /**\n * Trigger voice provider to respond\n */\n async answer(_options?: Record<string, unknown>): Promise<void> {\n this.validateConnectionState();\n\n // Send a signal to trigger response generation\n this.sendEvent('response.create', {});\n }\n\n /**\n * Equip the voice provider with instructions\n * @param instructions Instructions to add\n */\n addInstructions(instructions?: string): void {\n if (instructions) {\n this.options.instructions = instructions;\n this.log('Instructions added:', instructions);\n }\n }\n}\n\nexport type { IncrementalTurn } from './types';\n"],"mappings":"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"}