@ai-ecoverse/pi-audio 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/gemini-live/session.ts"],
4
- "sourcesContent": ["import type { JsonObject, Message, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from '../openai-realtime/socket.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface GeminiOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n vocabulary?: string[];\n}\n\nconst scheduling: Record<Delivery, string> = {\n interrupt: 'INTERRUPT',\n when_idle: 'WHEN_IDLE',\n silent: 'SILENT',\n};\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\ninterface IteratorResultEvent {\n value?: RealtimeEvent;\n done: boolean;\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): { secret: string; ephemeral: boolean } {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return { secret: token, ephemeral: true };\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return { secret: apiKey, ephemeral: false };\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(options: RealtimeOptions | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\n const extra = options?.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction sessionUrl(model: RealtimeModel<string>, secret: string, ephemeral: boolean): string {\n const url = new URL(model.baseUrl);\n if (ephemeral) url.searchParams.set('access_token', secret);\n else url.searchParams.set('key', secret);\n return url.toString();\n}\n\nfunction timeLeftMs(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.round(value * 1000);\n if (typeof value === 'string') {\n const matched = /^(\\d+(?:\\.\\d+)?)s$/.exec(value);\n if (matched !== null) return Math.round(Number(matched[1]) * 1000);\n const asNumber = Number(value);\n if (Number.isFinite(asNumber)) return Math.round(asNumber * 1000);\n }\n const duration = record(value);\n if (duration !== undefined && typeof duration.seconds === 'number') {\n const nanos = typeof duration.nanos === 'number' ? duration.nanos : 0;\n return Math.round(duration.seconds * 1000 + nanos / 1e6);\n }\n return 0;\n}\n\nfunction count(source: Record<string, unknown>, key: string): number {\n const value = source[key];\n return typeof value === 'number' ? value : 0;\n}\n\nfunction usageFrom(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = count(usage, 'promptTokenCount');\n const response = count(usage, 'responseTokenCount');\n const candidates = count(usage, 'candidatesTokenCount');\n const output = response > 0 ? response : candidates;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n }\n const object = record(value);\n if (object === undefined) return {};\n return object as JsonObject;\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction historyTurns(messages: Message[] | undefined): JsonObject[] {\n if (messages === undefined) return [];\n const turns: JsonObject[] = [];\n for (const message of messages) {\n if (message.role !== 'user' && message.role !== 'assistant') continue;\n const parts: JsonObject[] = [];\n for (const part of message.content) {\n if (typeof part === 'string') {\n parts.push({ text: part });\n continue;\n }\n if (part.type === 'text') parts.push({ text: part.text });\n }\n if (parts.length === 0) continue;\n turns.push({ role: message.role === 'assistant' ? 'model' : 'user', parts });\n }\n return turns;\n}\n\nfunction declaration(tool: Tool, model: RealtimeModel<string>): JsonObject {\n const fn: JsonObject = {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n if (model.id === 'gemini-live-2.5-flash-native-audio') fn.behavior = 'NON_BLOCKING';\n if (model.toolCalls === 'blocking') fn.behavior = 'BLOCKING';\n return fn;\n}\n\nfunction transcription(model: RealtimeModel<string>, vocabulary: string[] | undefined): JsonObject {\n if (model.id !== 'gemini-3.8-live') return {};\n if (vocabulary === undefined) return {};\n return { customVocabulary: vocabulary };\n}\n\nfunction setupBody(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n handle: string | undefined,\n vocabulary: string[] | undefined,\n turns: JsonObject[]\n): JsonObject {\n const setup: JsonObject = {\n model: `models/${model.id}`,\n generationConfig: { responseModalities: ['AUDIO'] },\n systemInstruction: { parts: [{ text: context.instructions }] },\n speechConfig: {\n voiceConfig: { prebuiltVoiceConfig: { voiceName: context.voice } },\n },\n inputAudioTranscription: transcription(model, vocabulary),\n outputAudioTranscription: {},\n contextWindowCompression: {\n triggerTokens: 100_000,\n slidingWindow: { targetTokens: 80_000 },\n },\n sessionResumption: handle === undefined ? {} : { handle },\n };\n if (context.tools.length > 0) {\n setup.tools = [{ functionDeclarations: context.tools.map((tool) => declaration(tool, model)) }];\n }\n if (turns.length > 0) setup.historyConfig = { initialHistoryInClientContent: true };\n return setup;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nclass GeminiSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #context: RealtimeContext;\n readonly #vocabulary: string[] | undefined;\n readonly #turns: JsonObject[];\n readonly #open: VoiceWireFactory;\n readonly #target: VoiceWireTarget;\n readonly #ledger = new RealtimeLedger();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResultEvent) => void> = [];\n readonly #outgoing: JsonObject[] = [];\n readonly #format: AudioFormat;\n #wire: VoiceWire;\n #pending: PendingAudio = { chunks: [], cursor: 0 };\n #item = 'g0';\n #turn = 0;\n #heard = '';\n #spoken = '';\n #handle: string | undefined;\n #tokens: Usage = blankUsage();\n #ready = false;\n #historySent = false;\n #closed = false;\n #ended = false;\n #meters: Usage | undefined;\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n vocabulary: string[] | undefined,\n open: VoiceWireFactory,\n target: VoiceWireTarget,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#vocabulary = vocabulary;\n this.#turns = historyTurns(context.history);\n this.#open = open;\n this.#target = target;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(handle: string | undefined): void {\n if (handle !== undefined && handle.length > 0) this.#handle = handle;\n this.#wire.send(JSON.stringify({ setup: this.#setup(handle) }));\n void this.#read(this.#wire);\n }\n\n #setup(handle: string | undefined): JsonObject {\n return setupBody(this.model, this.#context, handle, this.#vocabulary, this.#turns);\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #send(event: JsonObject): void {\n if (!this.#ready) {\n this.#outgoing.push(event);\n return;\n }\n this.#wire.send(JSON.stringify(event));\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n if (this.#ended) return;\n this.#ended = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({\n realtimeInput: {\n audio: {\n data: encodeAudioBase64(chunk.data),\n mimeType: `audio/pcm;rate=${String(chunk.format.sampleRate)}`,\n },\n },\n });\n }\n\n appendVideoFrame(jpeg: Uint8Array): void {\n this.#assertOpen();\n if (this.model.video === undefined) {\n throw new VoiceRequestError('error', 'video is unsupported');\n }\n this.#send({\n realtimeInput: { video: { data: encodeAudioBase64(jpeg), mimeType: 'image/jpeg' } },\n });\n }\n\n sendText(value: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(this.#client(value, true));\n }\n\n inject(value: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n if (delivery === 'silent') this.#send(this.#client(value, false));\n else if (delivery === 'when_idle') this.#send({ realtimeInput: { text: value } });\n else this.#send(this.#client(value, true));\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = () => {\n const mode = delivery === undefined ? 'interrupt' : delivery;\n this.#send({\n toolResponse: {\n functionResponses: [\n {\n id: callId,\n name: result.toolName,\n scheduling: scheduling[mode],\n response: {\n status: result.isError ? 'error' : 'ok',\n retryable: false,\n message: resultText(result),\n },\n },\n ],\n },\n });\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n planTruncation(this.model.truncation, heard);\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#meters !== undefined) return { usage: this.#meters };\n this.#closed = true;\n this.#ledger.settle();\n this.#wire.close();\n this.#end();\n this.#meters = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#meters };\n }\n\n #client(value: string, turnComplete: boolean): JsonObject {\n return {\n clientContent: {\n turns: [{ role: 'user', parts: [{ text: value }] }],\n turnComplete,\n },\n };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResultEvent>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #audio(encoded: string): void {\n if (encoded.length === 0) return;\n this.#pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #transcript(delta: string, final: boolean): void {\n const end = this.#pending.cursor + delta.length;\n const tagged = delta.length === 0 ? undefined : end;\n for (const event of takeAudio(this.#pending, this.#item, this.#format, tagged)) {\n this.#push(event);\n }\n this.#spoken += delta;\n this.#push({ type: 'output_transcript', itemId: this.#item, text: delta, final });\n }\n\n #input(transcript: string, final: boolean): void {\n if (transcript.length > 0) this.#heard = transcript;\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final });\n }\n\n #finish(interrupted: boolean): void {\n for (const event of takeAudio(this.#pending, this.#item, this.#format, undefined)) {\n this.#push(event);\n }\n if (this.#spoken.length > 0) {\n this.#push({\n type: 'output_transcript',\n itemId: this.#item,\n text: this.#spoken,\n final: true,\n });\n }\n if (this.#heard.length > 0) {\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final: true });\n }\n this.#push({\n type: 'response_done',\n itemId: this.#item,\n interrupted,\n usage: this.#tokens,\n });\n this.#tokens = blankUsage();\n this.#spoken = '';\n this.#heard = '';\n this.#turn += 1;\n this.#item = `g${String(this.#turn)}`;\n this.#pending = { chunks: [], cursor: 0 };\n }\n\n #onSetup(value: unknown): void {\n const done = record(value);\n const sessionId = done === undefined ? '' : text(done.sessionId);\n this.#push({ type: 'session_start', sessionId });\n if (!this.#historySent && this.#turns.length > 0) {\n this.#historySent = true;\n this.#wire.send(\n JSON.stringify({ clientContent: { turns: this.#turns, turnComplete: false } })\n );\n }\n this.#ready = true;\n const pending = this.#outgoing.splice(0);\n for (const event of pending) this.#wire.send(JSON.stringify(event));\n }\n\n #onResume(value: unknown): void {\n const update = record(value);\n if (update === undefined) return;\n const handle = text(update.newHandle);\n if (handle.length === 0 || update.resumable !== true) return;\n this.#handle = handle;\n this.#push({ type: 'resumption', handle });\n }\n\n #onGoAway(value: unknown): 'stop' | VoiceWire {\n const gone = record(value);\n const left = gone === undefined ? 0 : timeLeftMs(gone.timeLeft);\n this.#push({ type: 'go_away', timeLeftMs: left });\n const handle = this.#handle;\n if (handle === undefined) {\n this.#push({ type: 'error', error: 'session ended', fatal: true });\n return 'stop';\n }\n const next = this.#open(this.#target);\n this.#wire = next;\n this.#ready = false;\n next.send(JSON.stringify({ setup: this.#setup(handle) }));\n return next;\n }\n\n #onToolCall(value: unknown): void {\n const call = record(value);\n if (call === undefined || !Array.isArray(call.functionCalls)) return;\n for (const entry of call.functionCalls) {\n const fn = record(entry);\n if (fn === undefined) continue;\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id: text(fn.id),\n name: text(fn.name),\n arguments: argumentsOf(fn.args),\n },\n });\n }\n }\n\n #onCancel(value: unknown): void {\n const cancellation = record(value);\n if (cancellation === undefined || !Array.isArray(cancellation.ids)) return;\n for (const id of cancellation.ids) {\n if (typeof id !== 'string') continue;\n this.#push({ type: 'tool_call_cancelled', callId: id });\n }\n }\n\n #onServer(value: unknown): void {\n const content = record(value);\n if (content === undefined) return;\n this.#modelTurn(content.modelTurn);\n this.#caption(content.outputTranscription, 'output');\n this.#caption(content.inputTranscription, 'input');\n if (content.interrupted === true) {\n this.#push({ type: 'input_speech_start' });\n this.#finish(true);\n return;\n }\n if (content.turnComplete === true) this.#finish(false);\n }\n\n #modelTurn(value: unknown): void {\n const turn = record(value);\n if (turn === undefined || !Array.isArray(turn.parts)) return;\n for (const part of turn.parts) {\n const entry = record(part);\n if (entry === undefined) continue;\n const inline = record(entry.inlineData);\n if (inline === undefined) continue;\n if (!text(inline.mimeType).startsWith('audio/')) continue;\n this.#audio(text(inline.data));\n }\n }\n\n #caption(value: unknown, kind: 'input' | 'output'): void {\n const caption = record(value);\n if (caption === undefined) return;\n if (kind === 'input') this.#input(text(caption.text), false);\n else this.#transcript(text(caption.text), false);\n }\n\n #onError(value: unknown): void {\n const detail = record(value);\n if (detail === undefined) {\n const message = text(value);\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal: false,\n });\n return;\n }\n const code = text(detail.code);\n const message = text(detail.message);\n let fatal = false;\n if (code === 'MAX_DURATION' || code === 'SESSION_EXPIRED') fatal = true;\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal,\n });\n }\n\n #apply(event: Record<string, unknown>): 'ok' | 'stop' | VoiceWire {\n if (event.usageMetadata !== undefined) this.#tokens = usageFrom(event.usageMetadata);\n if (event.inputTranscription !== undefined) this.#caption(event.inputTranscription, 'input');\n if (event.outputTranscription !== undefined) this.#caption(event.outputTranscription, 'output');\n if (event.setupComplete !== undefined) this.#onSetup(event.setupComplete);\n if (event.sessionResumptionUpdate !== undefined) this.#onResume(event.sessionResumptionUpdate);\n if (event.toolCall !== undefined) this.#onToolCall(event.toolCall);\n if (event.toolCallCancellation !== undefined) this.#onCancel(event.toolCallCancellation);\n if (event.serverContent !== undefined) this.#onServer(event.serverContent);\n if (event.error !== undefined) this.#onError(event.error);\n if (event.goAway !== undefined) return this.#onGoAway(event.goAway);\n return 'ok';\n }\n\n #ingest(raw: string): 'ok' | 'stop' | VoiceWire {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n return 'ok';\n }\n const event = record(parsed);\n if (event === undefined) return 'ok';\n return this.#apply(event);\n }\n\n async #read(wire: VoiceWire): Promise<void> {\n let reconnect: VoiceWire | undefined;\n try {\n for await (const raw of wire.incoming) {\n const action = this.#ingest(raw);\n if (action === 'stop') return;\n if (action !== 'ok') {\n reconnect = action;\n return;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n if (reconnect !== undefined) {\n wire.close();\n void this.#read(reconnect);\n } else {\n this.#closed = true;\n this.#end();\n }\n }\n }\n}\n\nexport async function connectGeminiSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n gemini: GeminiOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n const { secret, ephemeral } = credential(options);\n const socket = realtimeSocket(sessionUrl(model, secret, ephemeral), options, []);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options),\n };\n const open =\n gemini.wire !== undefined\n ? gemini.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, gemini.socket);\n const session = new GeminiSession(model, context, gemini.vocabulary, open, target, open(target));\n const handle = options?.resumeHandle;\n session.start(handle);\n const signal = options?.signal;\n if (signal !== undefined) {\n signal.addEventListener('abort', () => void session.close(), { once: true });\n }\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { JsonObject, Message, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from '../openai-realtime/socket.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface GeminiOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n vocabulary?: string[];\n}\n\nconst scheduling: Record<Delivery, string> = {\n interrupt: 'INTERRUPT',\n when_idle: 'WHEN_IDLE',\n silent: 'SILENT',\n};\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\ninterface IteratorResultEvent {\n value?: RealtimeEvent;\n done: boolean;\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): { secret: string; ephemeral: boolean } {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return { secret: token, ephemeral: true };\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return { secret: apiKey, ephemeral: false };\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(options: RealtimeOptions | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\n const extra = options?.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction sessionUrl(model: RealtimeModel<string>, secret: string, ephemeral: boolean): string {\n const url = new URL(model.baseUrl);\n if (ephemeral) url.searchParams.set('access_token', secret);\n else url.searchParams.set('key', secret);\n return url.toString();\n}\n\nfunction timeLeftMs(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.round(value * 1000);\n if (typeof value === 'string') {\n const matched = /^(\\d+(?:\\.\\d+)?)s$/.exec(value);\n if (matched !== null) return Math.round(Number(matched[1]) * 1000);\n const asNumber = Number(value);\n if (Number.isFinite(asNumber)) return Math.round(asNumber * 1000);\n }\n const duration = record(value);\n if (duration !== undefined && typeof duration.seconds === 'number') {\n const nanos = typeof duration.nanos === 'number' ? duration.nanos : 0;\n return Math.round(duration.seconds * 1000 + nanos / 1e6);\n }\n return 0;\n}\n\nfunction count(source: Record<string, unknown>, key: string): number {\n const value = source[key];\n return typeof value === 'number' ? value : 0;\n}\n\nfunction usageFrom(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = count(usage, 'promptTokenCount');\n const response = count(usage, 'responseTokenCount');\n const candidates = count(usage, 'candidatesTokenCount');\n const output = response > 0 ? response : candidates;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n }\n const object = record(value);\n if (object === undefined) return {};\n return object as JsonObject;\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction historyTurns(messages: Message[] | undefined): JsonObject[] {\n if (messages === undefined) return [];\n const turns: JsonObject[] = [];\n for (const message of messages) {\n if (message.role !== 'user' && message.role !== 'assistant') continue;\n const parts: JsonObject[] = [];\n for (const part of message.content) {\n if (typeof part === 'string') {\n parts.push({ text: part });\n continue;\n }\n if (part.type === 'text') parts.push({ text: part.text });\n }\n if (parts.length === 0) continue;\n turns.push({ role: message.role === 'assistant' ? 'model' : 'user', parts });\n }\n return turns;\n}\n\nfunction declaration(tool: Tool, model: RealtimeModel<string>): JsonObject {\n const fn: JsonObject = {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n if (model.id === 'gemini-live-2.5-flash-native-audio') fn.behavior = 'NON_BLOCKING';\n if (model.toolCalls === 'blocking') fn.behavior = 'BLOCKING';\n return fn;\n}\n\nfunction transcription(model: RealtimeModel<string>, vocabulary: string[] | undefined): JsonObject {\n if (model.id !== 'gemini-3.8-live') return {};\n if (vocabulary === undefined) return {};\n return { customVocabulary: vocabulary };\n}\n\nfunction setupBody(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n handle: string | undefined,\n vocabulary: string[] | undefined,\n turns: JsonObject[]\n): JsonObject {\n const setup: JsonObject = {\n model: `models/${model.id}`,\n generationConfig: { responseModalities: ['AUDIO'] },\n systemInstruction: { parts: [{ text: context.instructions }] },\n speechConfig: {\n voiceConfig: { prebuiltVoiceConfig: { voiceName: context.voice } },\n },\n inputAudioTranscription: transcription(model, vocabulary),\n outputAudioTranscription: {},\n contextWindowCompression: {\n triggerTokens: 100_000,\n slidingWindow: { targetTokens: 80_000 },\n },\n sessionResumption: handle === undefined ? {} : { handle },\n };\n if (context.tools.length > 0) {\n setup.tools = [{ functionDeclarations: context.tools.map((tool) => declaration(tool, model)) }];\n }\n if (turns.length > 0) setup.historyConfig = { initialHistoryInClientContent: true };\n return setup;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nclass GeminiSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #context: RealtimeContext;\n readonly #vocabulary: string[] | undefined;\n readonly #turns: JsonObject[];\n readonly #open: VoiceWireFactory;\n readonly #target: VoiceWireTarget;\n readonly #ledger = new RealtimeLedger();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResultEvent) => void> = [];\n readonly #outgoing: JsonObject[] = [];\n readonly #format: AudioFormat;\n #wire: VoiceWire;\n #pending: PendingAudio = { chunks: [], cursor: 0 };\n #item = 'g0';\n #turn = 0;\n #heard = '';\n #spoken = '';\n #handle: string | undefined;\n #tokens: Usage = blankUsage();\n #ready = false;\n #historySent = false;\n #closed = false;\n #ended = false;\n #meters: Usage | undefined;\n readonly #toolGroups = new Map<string, { pending: Set<string>; responses: JsonObject[] }>();\n readonly #toolGroupOf = new Map<string, string>();\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n vocabulary: string[] | undefined,\n open: VoiceWireFactory,\n target: VoiceWireTarget,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#vocabulary = vocabulary;\n this.#turns = historyTurns(context.history);\n this.#open = open;\n this.#target = target;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(handle: string | undefined): void {\n if (handle !== undefined && handle.length > 0) this.#handle = handle;\n this.#wire.send(JSON.stringify({ setup: this.#setup(handle) }));\n void this.#read(this.#wire);\n }\n\n #setup(handle: string | undefined): JsonObject {\n return setupBody(this.model, this.#context, handle, this.#vocabulary, this.#turns);\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #send(event: JsonObject): void {\n if (!this.#ready) {\n this.#outgoing.push(event);\n return;\n }\n this.#wire.send(JSON.stringify(event));\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n if (this.#ended) return;\n this.#ended = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({\n realtimeInput: {\n audio: {\n data: encodeAudioBase64(chunk.data),\n mimeType: `audio/pcm;rate=${String(chunk.format.sampleRate)}`,\n },\n },\n });\n }\n\n appendVideoFrame(jpeg: Uint8Array): void {\n this.#assertOpen();\n if (this.model.video === undefined) {\n throw new VoiceRequestError('error', 'video is unsupported');\n }\n this.#send({\n realtimeInput: { video: { data: encodeAudioBase64(jpeg), mimeType: 'image/jpeg' } },\n });\n }\n\n sendText(value: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(this.#client(value, true));\n }\n\n inject(value: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n if (delivery === 'silent') this.#send(this.#client(value, false));\n else if (delivery === 'when_idle') this.#send({ realtimeInput: { text: value } });\n else this.#send(this.#client(value, true));\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = () => {\n const mode = delivery === undefined ? 'interrupt' : delivery;\n this.#queueTool(callId, {\n id: callId,\n name: result.toolName,\n scheduling: scheduling[mode],\n response: {\n status: result.isError ? 'error' : 'ok',\n retryable: false,\n message: resultText(result),\n },\n });\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n planTruncation(this.model.truncation, heard);\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#meters !== undefined) return { usage: this.#meters };\n this.#closed = true;\n this.#ledger.settle();\n this.#wire.close();\n this.#end();\n this.#meters = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#meters };\n }\n\n #client(value: string, turnComplete: boolean): JsonObject {\n return {\n clientContent: {\n turns: [{ role: 'user', parts: [{ text: value }] }],\n turnComplete,\n },\n };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResultEvent>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #audio(encoded: string): void {\n if (encoded.length === 0) return;\n this.#pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #transcript(delta: string, final: boolean): void {\n const end = this.#pending.cursor + delta.length;\n const tagged = delta.length === 0 ? undefined : end;\n for (const event of takeAudio(this.#pending, this.#item, this.#format, tagged)) {\n this.#push(event);\n }\n this.#spoken += delta;\n this.#push({ type: 'output_transcript', itemId: this.#item, text: delta, final });\n }\n\n #input(transcript: string, final: boolean): void {\n if (transcript.length > 0) this.#heard = transcript;\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final });\n }\n\n #finish(interrupted: boolean): void {\n for (const event of takeAudio(this.#pending, this.#item, this.#format, undefined)) {\n this.#push(event);\n }\n if (this.#spoken.length > 0) {\n this.#push({\n type: 'output_transcript',\n itemId: this.#item,\n text: this.#spoken,\n final: true,\n });\n }\n if (this.#heard.length > 0) {\n this.#push({ type: 'input_transcript', itemId: this.#item, text: this.#heard, final: true });\n }\n this.#push({\n type: 'response_done',\n itemId: this.#item,\n interrupted,\n usage: this.#tokens,\n });\n this.#tokens = blankUsage();\n this.#spoken = '';\n this.#heard = '';\n this.#turn += 1;\n this.#item = `g${String(this.#turn)}`;\n this.#pending = { chunks: [], cursor: 0 };\n }\n\n #onSetup(value: unknown): void {\n const done = record(value);\n const sessionId = done === undefined ? '' : text(done.sessionId);\n this.#push({ type: 'session_start', sessionId });\n if (!this.#historySent && this.#turns.length > 0) {\n this.#historySent = true;\n this.#wire.send(\n JSON.stringify({ clientContent: { turns: this.#turns, turnComplete: false } })\n );\n }\n this.#ready = true;\n const pending = this.#outgoing.splice(0);\n for (const event of pending) this.#wire.send(JSON.stringify(event));\n }\n\n #onResume(value: unknown): void {\n const update = record(value);\n if (update === undefined) return;\n const handle = text(update.newHandle);\n if (handle.length === 0 || update.resumable !== true) return;\n this.#handle = handle;\n this.#push({ type: 'resumption', handle });\n }\n\n #onGoAway(value: unknown): 'stop' | VoiceWire {\n const gone = record(value);\n const left = gone === undefined ? 0 : timeLeftMs(gone.timeLeft);\n this.#push({ type: 'go_away', timeLeftMs: left });\n const handle = this.#handle;\n if (handle === undefined) {\n this.#push({ type: 'error', error: 'session ended', fatal: true });\n return 'stop';\n }\n const next = this.#open(this.#target);\n this.#wire = next;\n this.#ready = false;\n next.send(JSON.stringify({ setup: this.#setup(handle) }));\n return next;\n }\n\n #queueTool(callId: string, response: JsonObject): void {\n const groupId = this.#toolGroupOf.get(callId) ?? '';\n const group = groupId.length === 0 ? undefined : this.#toolGroups.get(groupId);\n if (group === undefined) {\n this.#send({ toolResponse: { functionResponses: [response] } });\n return;\n }\n group.responses.push(response);\n group.pending.delete(callId);\n this.#toolGroupOf.delete(callId);\n if (group.pending.size > 0) return;\n this.#toolGroups.delete(groupId);\n this.#send({ toolResponse: { functionResponses: group.responses } });\n }\n\n #onToolCall(value: unknown): void {\n const call = record(value);\n if (call === undefined || !Array.isArray(call.functionCalls)) return;\n const ids: string[] = [];\n for (const entry of call.functionCalls) {\n const fn = record(entry);\n if (fn === undefined) continue;\n const id = text(fn.id);\n if (id.length > 0) ids.push(id);\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id,\n name: text(fn.name),\n arguments: argumentsOf(fn.args),\n },\n });\n }\n const groupId = ids.at(0);\n if (groupId === undefined) return;\n this.#toolGroups.set(groupId, { pending: new Set(ids), responses: [] });\n for (const id of ids) this.#toolGroupOf.set(id, groupId);\n }\n\n #onCancel(value: unknown): void {\n const cancellation = record(value);\n if (cancellation === undefined || !Array.isArray(cancellation.ids)) return;\n for (const id of cancellation.ids) {\n if (typeof id !== 'string') continue;\n this.#push({ type: 'tool_call_cancelled', callId: id });\n }\n }\n\n #onServer(value: unknown): void {\n const content = record(value);\n if (content === undefined) return;\n this.#modelTurn(content.modelTurn);\n this.#caption(content.outputTranscription, 'output');\n this.#caption(content.inputTranscription, 'input');\n if (content.interrupted === true) {\n this.#push({ type: 'input_speech_start' });\n this.#finish(true);\n return;\n }\n if (content.turnComplete === true) this.#finish(false);\n }\n\n #modelTurn(value: unknown): void {\n const turn = record(value);\n if (turn === undefined || !Array.isArray(turn.parts)) return;\n for (const part of turn.parts) {\n const entry = record(part);\n if (entry === undefined) continue;\n const inline = record(entry.inlineData);\n if (inline === undefined) continue;\n if (!text(inline.mimeType).startsWith('audio/')) continue;\n this.#audio(text(inline.data));\n }\n }\n\n #caption(value: unknown, kind: 'input' | 'output'): void {\n const caption = record(value);\n if (caption === undefined) return;\n if (kind === 'input') this.#input(text(caption.text), false);\n else this.#transcript(text(caption.text), false);\n }\n\n #onError(value: unknown): void {\n const detail = record(value);\n if (detail === undefined) {\n const message = text(value);\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal: false,\n });\n return;\n }\n const code = text(detail.code);\n const message = text(detail.message);\n let fatal = false;\n if (code === 'MAX_DURATION' || code === 'SESSION_EXPIRED') fatal = true;\n this.#push({\n type: 'error',\n error: message.length > 0 ? message : 'realtime error',\n fatal,\n });\n }\n\n #apply(event: Record<string, unknown>): 'ok' | 'stop' | VoiceWire {\n if (event.usageMetadata !== undefined) this.#tokens = usageFrom(event.usageMetadata);\n if (event.inputTranscription !== undefined) this.#caption(event.inputTranscription, 'input');\n if (event.outputTranscription !== undefined) this.#caption(event.outputTranscription, 'output');\n if (event.setupComplete !== undefined) this.#onSetup(event.setupComplete);\n if (event.sessionResumptionUpdate !== undefined) this.#onResume(event.sessionResumptionUpdate);\n if (event.toolCall !== undefined) this.#onToolCall(event.toolCall);\n if (event.toolCallCancellation !== undefined) this.#onCancel(event.toolCallCancellation);\n if (event.serverContent !== undefined) this.#onServer(event.serverContent);\n if (event.error !== undefined) this.#onError(event.error);\n if (event.goAway !== undefined) return this.#onGoAway(event.goAway);\n return 'ok';\n }\n\n #ingest(raw: string): 'ok' | 'stop' | VoiceWire {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n return 'ok';\n }\n const event = record(parsed);\n if (event === undefined) return 'ok';\n return this.#apply(event);\n }\n\n async #read(wire: VoiceWire): Promise<void> {\n let reconnect: VoiceWire | undefined;\n try {\n for await (const raw of wire.incoming) {\n const action = this.#ingest(raw);\n if (action === 'stop') return;\n if (action !== 'ok') {\n reconnect = action;\n return;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n if (reconnect !== undefined) {\n wire.close();\n void this.#read(reconnect);\n } else {\n this.#closed = true;\n this.#end();\n }\n }\n }\n}\n\nexport async function connectGeminiSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n gemini: GeminiOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n const { secret, ephemeral } = credential(options);\n const socket = realtimeSocket(sessionUrl(model, secret, ephemeral), options, []);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options),\n };\n const open =\n gemini.wire !== undefined\n ? gemini.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, gemini.socket);\n const session = new GeminiSession(model, context, gemini.vocabulary, open, target, open(target));\n const handle = options?.resumeHandle;\n session.start(handle);\n const signal = options?.signal;\n if (signal !== undefined) {\n signal.addEventListener('abort', () => void session.close(), { once: true });\n }\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["object", "message"]
7
7
  }
@@ -51,6 +51,7 @@ function openaiSession(context, model) {
51
51
  audio: {
52
52
  input: {
53
53
  format: { type: "audio/pcm", rate: model.inputFormat.sampleRate },
54
+ transcription: { model: "gpt-4o-mini-transcribe" },
54
55
  turn_detection: { type: "server_vad" }
55
56
  },
56
57
  output: {
@@ -68,7 +69,10 @@ function xaiSession(context, model, replace) {
68
69
  tools: context.tools.map(functionTool),
69
70
  resumption: { enabled: true },
70
71
  audio: {
71
- input: { format: { type: "audio/pcm", rate: model.inputFormat.sampleRate } },
72
+ input: {
73
+ format: { type: "audio/pcm", rate: model.inputFormat.sampleRate },
74
+ transcription: { model: "grok-transcribe" }
75
+ },
72
76
  output: { format: { type: "audio/pcm", rate: model.outputFormat.sampleRate } }
73
77
  }
74
78
  };
@@ -179,6 +183,9 @@ class VendorSession {
179
183
  #closed = false;
180
184
  #usage;
181
185
  #lastItem = "";
186
+ #busy = false;
187
+ #heldCreate = false;
188
+ #calls = /* @__PURE__ */ new Set();
182
189
  constructor(model, context, dialect, replace, wire) {
183
190
  this.model = model;
184
191
  this.#context = context;
@@ -202,7 +209,7 @@ class VendorSession {
202
209
  this.#assertOpen();
203
210
  this.#ledger.addText();
204
211
  this.#send(userItem(textValue));
205
- this.#send(responseCreate());
212
+ this.#armCreate(false);
206
213
  }
207
214
  inject(textValue, delivery) {
208
215
  this.#assertOpen();
@@ -216,7 +223,9 @@ class VendorSession {
216
223
  this.#assertOpen();
217
224
  const send = () => {
218
225
  this.#send(toolOutput(callId, result));
219
- if (delivery !== "silent") this.#send(responseCreate());
226
+ this.#calls.delete(callId);
227
+ if (delivery !== "silent") this.#armCreate(true);
228
+ else if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);
220
229
  };
221
230
  if (delivery === void 0) {
222
231
  send();
@@ -294,7 +303,7 @@ class VendorSession {
294
303
  return;
295
304
  }
296
305
  this.#send(userItem(textValue));
297
- if (delivery !== "silent") this.#send(responseCreate());
306
+ if (delivery !== "silent") this.#armCreate(false);
298
307
  }
299
308
  #send(event) {
300
309
  this.#wire.send(JSON.stringify(event));
@@ -329,12 +338,26 @@ class VendorSession {
329
338
  timestamp: 0
330
339
  });
331
340
  }
332
- #transcript(itemId, delta, final) {
341
+ #armCreate(waitForCalls) {
342
+ if (this.#busy || waitForCalls && this.#calls.size > 0) {
343
+ this.#heldCreate = true;
344
+ return;
345
+ }
346
+ this.#heldCreate = false;
347
+ this.#busy = true;
348
+ this.#send(responseCreate());
349
+ }
350
+ #releaseResponse() {
351
+ this.#busy = false;
352
+ if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);
353
+ }
354
+ #transcript(itemId, value, final) {
333
355
  const pending = this.#pendingFor(itemId);
334
- const end = pending.cursor + delta.length;
335
- const tagged = delta.length === 0 ? void 0 : end;
356
+ const addition = final ? value.slice(pending.cursor) : value;
357
+ const end = pending.cursor + addition.length;
358
+ const tagged = addition.length === 0 ? void 0 : end;
336
359
  for (const event of takeAudio(pending, itemId, this.#format, tagged)) this.#push(event);
337
- this.#push({ type: "output_transcript", itemId, text: delta, final });
360
+ this.#push({ type: "output_transcript", itemId, text: value, final });
338
361
  }
339
362
  #input(itemId, transcript, mode, final) {
340
363
  const previous = this.#inputText.get(itemId) ?? "";
@@ -348,18 +371,21 @@ class VendorSession {
348
371
  return text(object[key]);
349
372
  }
350
373
  #functionCall(event) {
374
+ const id = text(event.call_id);
375
+ if (id.length > 0) this.#calls.add(id);
351
376
  this.#push({
352
377
  type: "tool_call",
353
378
  blocking: this.model.toolCalls === "blocking",
354
379
  call: {
355
380
  type: "toolCall",
356
- id: text(event.call_id),
381
+ id,
357
382
  name: text(event.name),
358
383
  arguments: argumentsOf(event.arguments)
359
384
  }
360
385
  });
361
386
  }
362
387
  #responseDone(event) {
388
+ this.#releaseResponse();
363
389
  const response = record(event.response);
364
390
  const output = response === void 0 ? void 0 : response.output;
365
391
  const first = Array.isArray(output) ? record(output[0]) : void 0;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/openai-realtime/session.ts"],
4
- "sourcesContent": ["import type { JsonObject, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from './socket.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface AdapterOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n replace?: Record<string, string>;\n}\n\nexport interface VendorSessionOptions extends AdapterOptions {\n dialect: 'openai' | 'xai';\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): string {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return token;\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return apiKey;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(\n options: RealtimeOptions | undefined,\n secret: string,\n bearer: boolean\n): Record<string, string> {\n const headers: Record<string, string> = {};\n if (bearer) headers.Authorization = `Bearer ${secret}`;\n const extra = options?.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction sessionUrl(model: RealtimeModel<string>, resumeHandle: string | undefined): string {\n const params = new URLSearchParams({ model: model.id });\n if (resumeHandle !== undefined) params.set('conversation_id', resumeHandle);\n return `${model.baseUrl}?${params.toString()}`;\n}\n\nfunction functionTool(tool: Tool): JsonObject {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n}\n\nfunction openaiSession(context: RealtimeContext, model: RealtimeModel<string>): JsonObject {\n return {\n type: 'realtime',\n instructions: context.instructions,\n tools: context.tools.map(functionTool),\n audio: {\n input: {\n format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate },\n turn_detection: { type: 'server_vad' },\n },\n output: {\n format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate },\n voice: context.voice,\n },\n },\n };\n}\n\nfunction xaiSession(\n context: RealtimeContext,\n model: RealtimeModel<string>,\n replace: Record<string, string> | undefined\n): JsonObject {\n const session: JsonObject = {\n voice: context.voice,\n instructions: context.instructions,\n turn_detection: { type: 'server_vad' },\n tools: context.tools.map(functionTool),\n resumption: { enabled: true },\n audio: {\n input: { format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate } },\n output: { format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate } },\n },\n };\n if (replace !== undefined) session.replace = replace;\n return session;\n}\n\nfunction userItem(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: value }],\n },\n };\n}\n\nfunction responseCreate(): JsonObject {\n return { type: 'response.create' };\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction toolOutput(callId: string, result: ToolResultMessage): JsonObject {\n return {\n type: 'conversation.item.create',\n item: { type: 'function_call_output', call_id: callId, output: resultText(result) },\n };\n}\n\nfunction forceMessage(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'force_message',\n role: 'assistant',\n content: [{ type: 'output_text', text: value }],\n },\n };\n}\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined || total === 0) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value !== 'string') return {};\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n}\n\nfunction responseUsage(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;\n const output = typeof usage.output_tokens === 'number' ? usage.output_tokens : 0;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nclass VendorSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #dialect: 'openai' | 'xai';\n readonly #context: RealtimeContext;\n readonly #replace: Record<string, string> | undefined;\n readonly #wire: VoiceWire;\n readonly #ledger = new RealtimeLedger();\n readonly #format: AudioFormat;\n readonly #pending = new Map<string, PendingAudio>();\n readonly #inputText = new Map<string, string>();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResult<RealtimeEvent>) => void> = [];\n #closed = false;\n #usage: Usage | undefined;\n #lastItem = '';\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n dialect: 'openai' | 'xai',\n replace: Record<string, string> | undefined,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#dialect = dialect;\n this.#replace = replace;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(options: RealtimeOptions | undefined): void {\n this.#send(this.#sessionUpdate(this.#context));\n const signal = options?.signal;\n if (signal !== undefined) signal.addEventListener('abort', () => void this.close());\n void this.#read();\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({ type: 'input_audio_buffer.append', audio: encodeAudioBase64(chunk.data) });\n }\n\n sendText(textValue: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(userItem(textValue));\n this.#send(responseCreate());\n }\n\n inject(textValue: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n this.#sendInject(textValue, delivery);\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = (): void => {\n this.#send(toolOutput(callId, result));\n if (delivery !== 'silent') this.#send(responseCreate());\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n this.#send({ type: 'response.cancel' });\n const plan = planTruncation(this.model.truncation, heard);\n if (plan.item !== undefined) {\n this.#send({\n type: 'conversation.item.truncate',\n item_id: plan.item.itemId,\n content_index: 0,\n audio_end_ms: plan.item.audioEndMs,\n });\n }\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n let instructions = this.#context.instructions;\n if (patch.instructions !== undefined) instructions = patch.instructions;\n let tools = this.#context.tools;\n if (patch.tools !== undefined) tools = patch.tools;\n let voice = this.#context.voice;\n if (patch.voice !== undefined) voice = patch.voice;\n const context: RealtimeContext = { instructions, tools, voice };\n if (patch.instructions !== undefined) this.#context.instructions = context.instructions;\n if (patch.tools !== undefined) this.#context.tools = context.tools;\n if (patch.voice !== undefined) this.#context.voice = context.voice;\n this.#send(this.#sessionUpdate(context));\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#usage !== undefined) return { usage: this.#usage };\n this.#ledger.settle();\n this.#closed = true;\n this.#wire.close();\n this.#end();\n this.#usage = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#usage };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResult<RealtimeEvent>>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #sessionUpdate(context: RealtimeContext): JsonObject {\n if (this.#dialect === 'openai') {\n return { type: 'session.update', session: openaiSession(context, this.model) };\n }\n return { type: 'session.update', session: xaiSession(context, this.model, this.#replace) };\n }\n\n #sendInject(textValue: string, delivery: Delivery): void {\n if (this.#dialect === 'xai' && delivery !== 'silent') {\n this.#send(forceMessage(textValue));\n return;\n }\n this.#send(userItem(textValue));\n if (delivery !== 'silent') this.#send(responseCreate());\n }\n\n #send(event: JsonObject): void {\n this.#wire.send(JSON.stringify(event));\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n for (const waiter of this.#waiters) waiter({ value: undefined, done: true });\n this.#waiters.length = 0;\n }\n\n #pendingFor(itemId: string): PendingAudio {\n const existing = this.#pending.get(itemId);\n if (existing !== undefined) return existing;\n const created = { chunks: [], cursor: 0 };\n this.#pending.set(itemId, created);\n return created;\n }\n\n #audio(itemId: string, encoded: string): void {\n this.#lastItem = itemId;\n const pending = this.#pendingFor(itemId);\n pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #transcript(itemId: string, delta: string, final: boolean): void {\n const pending = this.#pendingFor(itemId);\n const end = pending.cursor + delta.length;\n const tagged = delta.length === 0 ? undefined : end;\n for (const event of takeAudio(pending, itemId, this.#format, tagged)) this.#push(event);\n this.#push({ type: 'output_transcript', itemId, text: delta, final });\n }\n\n #input(itemId: string, transcript: string, mode: 'append' | 'replace', final: boolean): void {\n const previous = this.#inputText.get(itemId) ?? '';\n const next = mode === 'append' ? `${previous}${transcript}` : transcript;\n this.#inputText.set(itemId, next);\n this.#push({ type: 'input_transcript', itemId, text: next, final });\n }\n\n #field(value: unknown, key: string): string {\n const object = record(value);\n if (object === undefined) return '';\n return text(object[key]);\n }\n\n #functionCall(event: Record<string, unknown>): void {\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id: text(event.call_id),\n name: text(event.name),\n arguments: argumentsOf(event.arguments),\n },\n });\n }\n\n #responseDone(event: Record<string, unknown>): void {\n const response = record(event.response);\n const output = response === undefined ? undefined : response.output;\n const first = Array.isArray(output) ? record(output[0]) : undefined;\n let itemId = this.#lastItem;\n if (first !== undefined && text(first.id).length > 0) itemId = text(first.id);\n const pending = this.#pendingFor(itemId);\n for (const audioEvent of takeAudio(pending, itemId, this.#format, undefined)) {\n this.#push(audioEvent);\n }\n const status = response === undefined ? '' : text(response.status);\n const usage = response === undefined ? undefined : response.usage;\n this.#push({\n type: 'response_done',\n itemId,\n interrupted: status === 'cancelled',\n usage: responseUsage(usage),\n });\n }\n\n #serverError(event: Record<string, unknown>): void {\n const detail = record(event.error);\n const code = detail === undefined ? '' : text(detail.code);\n const message = detail === undefined ? '' : text(detail.message);\n let fatal = false;\n if (code === 'max_duration') fatal = true;\n if (code === 'session_expired') fatal = true;\n this.#push({ type: 'error', error: message, fatal });\n }\n\n #apply(event: Record<string, unknown>): void {\n const type = text(event.type);\n if (type === 'session.created') {\n this.#push({ type: 'session_start', sessionId: this.#field(event.session, 'id') });\n return;\n }\n if (type === 'conversation.created') {\n this.#push({ type: 'resumption', handle: this.#field(event.conversation, 'id') });\n return;\n }\n if (type === 'input_audio_buffer.speech_started') {\n this.#push({ type: 'input_speech_start' });\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.delta') {\n this.#input(text(event.item_id), text(event.delta), 'append', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.updated') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.completed') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', true);\n return;\n }\n if (type === 'response.output_audio.delta') {\n this.#audio(text(event.item_id), text(event.delta));\n return;\n }\n if (type === 'response.output_audio_transcript.delta') {\n this.#transcript(text(event.item_id), text(event.delta), false);\n return;\n }\n if (type === 'response.output_audio_transcript.done') {\n this.#transcript(text(event.item_id), text(event.transcript), true);\n return;\n }\n if (type === 'response.function_call_arguments.done') {\n this.#functionCall(event);\n return;\n }\n if (type === 'response.done') {\n this.#responseDone(event);\n return;\n }\n if (type === 'error') this.#serverError(event);\n }\n\n async #read(): Promise<void> {\n try {\n for await (const raw of this.#wire.incoming) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n continue;\n }\n const event = record(parsed);\n if (event === undefined || typeof event.type !== 'string') continue;\n this.#apply(event);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n this.#closed = true;\n this.#end();\n }\n }\n}\n\nexport async function connectVendorSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n vendor: VendorSessionOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n if (options?.transport === 'webrtc' && vendor.wire === undefined) {\n throw new VoiceRequestError('error', 'webrtc requires a data channel wire');\n }\n const secret = credential(options);\n const token = options?.token?.value;\n const ephemeral = typeof token === 'string' && token.length > 0;\n const protocols: string[] = [];\n if (vendor.dialect === 'openai') protocols.push(`openai-insecure-api-key.${secret}`, 'realtime');\n else if (ephemeral) protocols.push(`xai-client-secret.${secret}`);\n else protocols.push('realtime');\n const resumeHandle = vendor.dialect === 'xai' ? options?.resumeHandle : undefined;\n const socket = realtimeSocket(sessionUrl(model, resumeHandle), options, protocols);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options, secret, vendor.dialect === 'openai' || !ephemeral),\n };\n const open =\n vendor.wire !== undefined\n ? vendor.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, vendor.socket);\n const session = new VendorSession(model, context, vendor.dialect, vendor.replace, open(target));\n session.start(options);\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { JsonObject, Tool, ToolResultMessage, Usage } from '@earendil-works/pi-ai';\nimport type { AudioChunk, AudioFormat, TextSpan } from '../audio.ts';\nimport { blankUsage } from '../blank-usage.ts';\nimport { decodeAudioBase64, encodeAudioBase64 } from '../convert.ts';\nimport { VoiceRequestError } from '../errors.ts';\nimport type { Delivery, RealtimeModel } from '../models.ts';\nimport type {\n RealtimeContext,\n RealtimeEvent,\n RealtimeOptions,\n RealtimeSession,\n} from '../realtime.ts';\nimport { planTruncation, RealtimeLedger, realtimeSocket } from '../realtime.ts';\nimport {\n openVoiceSocket,\n type VoiceSocketConstructor,\n type VoiceWire,\n type VoiceWireTarget,\n} from './socket.ts';\n\nexport type VoiceWireFactory = (target: VoiceWireTarget) => VoiceWire;\n\nexport interface AdapterOptions {\n wire?: VoiceWireFactory;\n socket?: VoiceSocketConstructor;\n replace?: Record<string, string>;\n}\n\nexport interface VendorSessionOptions extends AdapterOptions {\n dialect: 'openai' | 'xai';\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction credential(options: RealtimeOptions | undefined): string {\n const token = options?.token?.value;\n if (typeof token === 'string' && token.length > 0) return token;\n const apiKey = options?.apiKey;\n if (typeof apiKey === 'string' && apiKey.length > 0) return apiKey;\n throw new VoiceRequestError('error', 'missing realtime credential');\n}\n\nfunction headersFor(\n options: RealtimeOptions | undefined,\n secret: string,\n bearer: boolean\n): Record<string, string> {\n const headers: Record<string, string> = {};\n if (bearer) headers.Authorization = `Bearer ${secret}`;\n const extra = options?.headers;\n if (extra === undefined) return headers;\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) delete headers[key];\n else headers[key] = value;\n }\n return headers;\n}\n\nfunction sessionUrl(model: RealtimeModel<string>, resumeHandle: string | undefined): string {\n const params = new URLSearchParams({ model: model.id });\n if (resumeHandle !== undefined) params.set('conversation_id', resumeHandle);\n return `${model.baseUrl}?${params.toString()}`;\n}\n\nfunction functionTool(tool: Tool): JsonObject {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters as unknown as JsonObject,\n };\n}\n\nfunction openaiSession(context: RealtimeContext, model: RealtimeModel<string>): JsonObject {\n return {\n type: 'realtime',\n instructions: context.instructions,\n tools: context.tools.map(functionTool),\n audio: {\n input: {\n format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate },\n transcription: { model: 'gpt-4o-mini-transcribe' },\n turn_detection: { type: 'server_vad' },\n },\n output: {\n format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate },\n voice: context.voice,\n },\n },\n };\n}\n\nfunction xaiSession(\n context: RealtimeContext,\n model: RealtimeModel<string>,\n replace: Record<string, string> | undefined\n): JsonObject {\n const session: JsonObject = {\n voice: context.voice,\n instructions: context.instructions,\n turn_detection: { type: 'server_vad' },\n tools: context.tools.map(functionTool),\n resumption: { enabled: true },\n audio: {\n input: {\n format: { type: 'audio/pcm', rate: model.inputFormat.sampleRate },\n transcription: { model: 'grok-transcribe' },\n },\n output: { format: { type: 'audio/pcm', rate: model.outputFormat.sampleRate } },\n },\n };\n if (replace !== undefined) session.replace = replace;\n return session;\n}\n\nfunction userItem(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: value }],\n },\n };\n}\n\nfunction responseCreate(): JsonObject {\n return { type: 'response.create' };\n}\n\nfunction resultText(result: ToolResultMessage): string {\n const parts: string[] = [];\n for (const part of result.content) {\n if (part.type === 'text') parts.push(part.text);\n }\n return parts.join('\\n');\n}\n\nfunction toolOutput(callId: string, result: ToolResultMessage): JsonObject {\n return {\n type: 'conversation.item.create',\n item: { type: 'function_call_output', call_id: callId, output: resultText(result) },\n };\n}\n\nfunction forceMessage(value: string): JsonObject {\n return {\n type: 'conversation.item.create',\n item: {\n type: 'force_message',\n role: 'assistant',\n content: [{ type: 'output_text', text: value }],\n },\n };\n}\n\ninterface PendingAudio {\n chunks: AudioChunk[];\n cursor: number;\n}\n\nfunction spanFor(itemId: string, start: number, end: number): TextSpan {\n return { itemId, start, end };\n}\n\nfunction takeAudio(\n pending: PendingAudio,\n itemId: string,\n format: AudioFormat,\n end: number | undefined\n): RealtimeEvent[] {\n const chunks = pending.chunks;\n pending.chunks = [];\n if (chunks.length === 0) return [];\n const start = pending.cursor;\n if (end !== undefined) pending.cursor = end;\n const width = end === undefined ? 0 : end - start;\n const total = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);\n let used = 0;\n let cursor = start;\n return chunks.map((chunk, index) => {\n const event: RealtimeEvent = {\n type: 'output_audio',\n itemId,\n chunk: { data: chunk.data, format, timestamp: chunk.timestamp },\n };\n if (end === undefined || total === 0) return event;\n const last = index === chunks.length - 1;\n const next = last ? end : start + Math.round(((used + chunk.data.length) / total) * width);\n used += chunk.data.length;\n event.span = spanFor(itemId, cursor, next);\n cursor = next;\n return event;\n });\n}\n\nfunction argumentsOf(value: unknown): JsonObject {\n if (typeof value !== 'string') return {};\n try {\n const parsed: unknown = JSON.parse(value);\n const object = record(parsed);\n if (object === undefined) return {};\n return object as JsonObject;\n } catch {\n return {};\n }\n}\n\nfunction responseUsage(value: unknown): Usage {\n const usage = record(value);\n if (usage === undefined) return blankUsage();\n const input = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;\n const output = typeof usage.output_tokens === 'number' ? usage.output_tokens : 0;\n return {\n input,\n output,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: input + output,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nclass VendorSession implements RealtimeSession {\n readonly model: RealtimeModel<string>;\n readonly #dialect: 'openai' | 'xai';\n readonly #context: RealtimeContext;\n readonly #replace: Record<string, string> | undefined;\n readonly #wire: VoiceWire;\n readonly #ledger = new RealtimeLedger();\n readonly #format: AudioFormat;\n readonly #pending = new Map<string, PendingAudio>();\n readonly #inputText = new Map<string, string>();\n readonly #queue: RealtimeEvent[] = [];\n readonly #waiters: Array<(result: IteratorResult<RealtimeEvent>) => void> = [];\n #closed = false;\n #usage: Usage | undefined;\n #lastItem = '';\n #busy = false;\n #heldCreate = false;\n readonly #calls = new Set<string>();\n\n constructor(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n dialect: 'openai' | 'xai',\n replace: Record<string, string> | undefined,\n wire: VoiceWire\n ) {\n this.model = model;\n this.#context = context;\n this.#dialect = dialect;\n this.#replace = replace;\n this.#wire = wire;\n this.#format = model.outputFormat;\n }\n\n start(options: RealtimeOptions | undefined): void {\n this.#send(this.#sessionUpdate(this.#context));\n const signal = options?.signal;\n if (signal !== undefined) signal.addEventListener('abort', () => void this.close());\n void this.#read();\n }\n\n appendAudio(chunk: AudioChunk): void {\n this.#assertOpen();\n this.#ledger.addInput(chunk);\n this.#send({ type: 'input_audio_buffer.append', audio: encodeAudioBase64(chunk.data) });\n }\n\n sendText(textValue: string): void {\n this.#assertOpen();\n this.#ledger.addText();\n this.#send(userItem(textValue));\n this.#armCreate(false);\n }\n\n inject(textValue: string, delivery: Delivery): void {\n this.#assertOpen();\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n this.#ledger.addText();\n this.#sendInject(textValue, delivery);\n });\n }\n\n respond(callId: string, result: ToolResultMessage, delivery?: Delivery): void {\n this.#assertOpen();\n const send = (): void => {\n this.#send(toolOutput(callId, result));\n this.#calls.delete(callId);\n if (delivery !== 'silent') this.#armCreate(true);\n else if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);\n };\n if (delivery === undefined) {\n send();\n return;\n }\n this.#ledger.submit(this.model.delivery, delivery, () => {\n this.#assertOpen();\n send();\n });\n }\n\n interrupt(heard?: { itemId: string; heardMs: number }): void {\n this.#assertOpen();\n this.#send({ type: 'response.cancel' });\n const plan = planTruncation(this.model.truncation, heard);\n if (plan.item !== undefined) {\n this.#send({\n type: 'conversation.item.truncate',\n item_id: plan.item.itemId,\n content_index: 0,\n audio_end_ms: plan.item.audioEndMs,\n });\n }\n this.#ledger.settle();\n }\n\n update(patch: Partial<Pick<RealtimeContext, 'instructions' | 'tools' | 'voice'>>): void {\n this.#assertOpen();\n if (patch.tools !== undefined && !this.model.liveToolUpdate) {\n throw new VoiceRequestError('error', 'live tool update is unsupported');\n }\n let instructions = this.#context.instructions;\n if (patch.instructions !== undefined) instructions = patch.instructions;\n let tools = this.#context.tools;\n if (patch.tools !== undefined) tools = patch.tools;\n let voice = this.#context.voice;\n if (patch.voice !== undefined) voice = patch.voice;\n const context: RealtimeContext = { instructions, tools, voice };\n if (patch.instructions !== undefined) this.#context.instructions = context.instructions;\n if (patch.tools !== undefined) this.#context.tools = context.tools;\n if (patch.voice !== undefined) this.#context.voice = context.voice;\n this.#send(this.#sessionUpdate(context));\n }\n\n async close(): Promise<{ usage: Usage }> {\n if (this.#usage !== undefined) return { usage: this.#usage };\n this.#ledger.settle();\n this.#closed = true;\n this.#wire.close();\n this.#end();\n this.#usage = this.#ledger.usage(this.model.audioCost);\n return { usage: this.#usage };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n while (true) {\n const queued = this.#queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (this.#closed) return;\n const next = await new Promise<IteratorResult<RealtimeEvent>>((resolve) => {\n this.#waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n }\n\n #sessionUpdate(context: RealtimeContext): JsonObject {\n if (this.#dialect === 'openai') {\n return { type: 'session.update', session: openaiSession(context, this.model) };\n }\n return { type: 'session.update', session: xaiSession(context, this.model, this.#replace) };\n }\n\n #sendInject(textValue: string, delivery: Delivery): void {\n if (this.#dialect === 'xai' && delivery !== 'silent') {\n this.#send(forceMessage(textValue));\n return;\n }\n this.#send(userItem(textValue));\n if (delivery !== 'silent') this.#armCreate(false);\n }\n\n #send(event: JsonObject): void {\n this.#wire.send(JSON.stringify(event));\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new VoiceRequestError('error', 'session is closed');\n }\n\n #push(event: RealtimeEvent): void {\n if (this.#closed) return;\n this.#ledger.observe(event);\n const waiter = this.#waiters.shift();\n if (waiter !== undefined) waiter({ value: event, done: false });\n else this.#queue.push(event);\n }\n\n #end(): void {\n for (const waiter of this.#waiters) waiter({ value: undefined, done: true });\n this.#waiters.length = 0;\n }\n\n #pendingFor(itemId: string): PendingAudio {\n const existing = this.#pending.get(itemId);\n if (existing !== undefined) return existing;\n const created = { chunks: [], cursor: 0 };\n this.#pending.set(itemId, created);\n return created;\n }\n\n #audio(itemId: string, encoded: string): void {\n this.#lastItem = itemId;\n const pending = this.#pendingFor(itemId);\n pending.chunks.push({\n data: decodeAudioBase64(encoded),\n format: this.#format,\n timestamp: 0,\n });\n }\n\n #armCreate(waitForCalls: boolean): void {\n if (this.#busy || (waitForCalls && this.#calls.size > 0)) {\n this.#heldCreate = true;\n return;\n }\n this.#heldCreate = false;\n this.#busy = true;\n this.#send(responseCreate());\n }\n\n #releaseResponse(): void {\n this.#busy = false;\n if (this.#heldCreate && this.#calls.size === 0) this.#armCreate(false);\n }\n\n #transcript(itemId: string, value: string, final: boolean): void {\n const pending = this.#pendingFor(itemId);\n const addition = final ? value.slice(pending.cursor) : value;\n const end = pending.cursor + addition.length;\n const tagged = addition.length === 0 ? undefined : end;\n for (const event of takeAudio(pending, itemId, this.#format, tagged)) this.#push(event);\n this.#push({ type: 'output_transcript', itemId, text: value, final });\n }\n\n #input(itemId: string, transcript: string, mode: 'append' | 'replace', final: boolean): void {\n const previous = this.#inputText.get(itemId) ?? '';\n const next = mode === 'append' ? `${previous}${transcript}` : transcript;\n this.#inputText.set(itemId, next);\n this.#push({ type: 'input_transcript', itemId, text: next, final });\n }\n\n #field(value: unknown, key: string): string {\n const object = record(value);\n if (object === undefined) return '';\n return text(object[key]);\n }\n\n #functionCall(event: Record<string, unknown>): void {\n const id = text(event.call_id);\n if (id.length > 0) this.#calls.add(id);\n this.#push({\n type: 'tool_call',\n blocking: this.model.toolCalls === 'blocking',\n call: {\n type: 'toolCall',\n id,\n name: text(event.name),\n arguments: argumentsOf(event.arguments),\n },\n });\n }\n\n #responseDone(event: Record<string, unknown>): void {\n this.#releaseResponse();\n const response = record(event.response);\n const output = response === undefined ? undefined : response.output;\n const first = Array.isArray(output) ? record(output[0]) : undefined;\n let itemId = this.#lastItem;\n if (first !== undefined && text(first.id).length > 0) itemId = text(first.id);\n const pending = this.#pendingFor(itemId);\n for (const audioEvent of takeAudio(pending, itemId, this.#format, undefined)) {\n this.#push(audioEvent);\n }\n const status = response === undefined ? '' : text(response.status);\n const usage = response === undefined ? undefined : response.usage;\n this.#push({\n type: 'response_done',\n itemId,\n interrupted: status === 'cancelled',\n usage: responseUsage(usage),\n });\n }\n\n #serverError(event: Record<string, unknown>): void {\n const detail = record(event.error);\n const code = detail === undefined ? '' : text(detail.code);\n const message = detail === undefined ? '' : text(detail.message);\n let fatal = false;\n if (code === 'max_duration') fatal = true;\n if (code === 'session_expired') fatal = true;\n this.#push({ type: 'error', error: message, fatal });\n }\n\n #apply(event: Record<string, unknown>): void {\n const type = text(event.type);\n if (type === 'session.created') {\n this.#push({ type: 'session_start', sessionId: this.#field(event.session, 'id') });\n return;\n }\n if (type === 'conversation.created') {\n this.#push({ type: 'resumption', handle: this.#field(event.conversation, 'id') });\n return;\n }\n if (type === 'input_audio_buffer.speech_started') {\n this.#push({ type: 'input_speech_start' });\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.delta') {\n this.#input(text(event.item_id), text(event.delta), 'append', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.updated') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', false);\n return;\n }\n if (type === 'conversation.item.input_audio_transcription.completed') {\n this.#input(text(event.item_id), text(event.transcript), 'replace', true);\n return;\n }\n if (type === 'response.output_audio.delta') {\n this.#audio(text(event.item_id), text(event.delta));\n return;\n }\n if (type === 'response.output_audio_transcript.delta') {\n this.#transcript(text(event.item_id), text(event.delta), false);\n return;\n }\n if (type === 'response.output_audio_transcript.done') {\n this.#transcript(text(event.item_id), text(event.transcript), true);\n return;\n }\n if (type === 'response.function_call_arguments.done') {\n this.#functionCall(event);\n return;\n }\n if (type === 'response.done') {\n this.#responseDone(event);\n return;\n }\n if (type === 'error') this.#serverError(event);\n }\n\n async #read(): Promise<void> {\n try {\n for await (const raw of this.#wire.incoming) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.#push({ type: 'error', error: 'invalid realtime event', fatal: false });\n continue;\n }\n const event = record(parsed);\n if (event === undefined || typeof event.type !== 'string') continue;\n this.#apply(event);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.#push({ type: 'error', error: message, fatal: true });\n } finally {\n this.#closed = true;\n this.#end();\n }\n }\n}\n\nexport async function connectVendorSession(\n model: RealtimeModel<string>,\n context: RealtimeContext,\n options: RealtimeOptions | undefined,\n vendor: VendorSessionOptions\n): Promise<RealtimeSession> {\n if (options?.signal?.aborted === true) {\n throw new VoiceRequestError('aborted', 'session aborted');\n }\n if (options?.transport === 'webrtc' && vendor.wire === undefined) {\n throw new VoiceRequestError('error', 'webrtc requires a data channel wire');\n }\n const secret = credential(options);\n const token = options?.token?.value;\n const ephemeral = typeof token === 'string' && token.length > 0;\n const protocols: string[] = [];\n if (vendor.dialect === 'openai') protocols.push(`openai-insecure-api-key.${secret}`, 'realtime');\n else if (ephemeral) protocols.push(`xai-client-secret.${secret}`);\n else protocols.push('realtime');\n const resumeHandle = vendor.dialect === 'xai' ? options?.resumeHandle : undefined;\n const socket = realtimeSocket(sessionUrl(model, resumeHandle), options, protocols);\n const target: VoiceWireTarget = {\n url: socket.url,\n protocols: socket.protocols,\n headers: headersFor(options, secret, vendor.dialect === 'openai' || !ephemeral),\n };\n const open =\n vendor.wire !== undefined\n ? vendor.wire\n : (wireTarget: VoiceWireTarget) => openVoiceSocket(wireTarget, vendor.socket);\n const session = new VendorSession(model, context, vendor.dialect, vendor.replace, open(target));\n session.start(options);\n return session;\n}\n\nexport function pcmFormat(sampleRate: number): AudioFormat {\n return { encoding: 'pcm16', sampleRate, channels: 1 };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,8 +4,10 @@ function openVoiceSocket(target, Socket = WebSocket) {
4
4
  headers: target.headers
5
5
  });
6
6
  const queue = [];
7
+ const outgoing = [];
7
8
  const waiters = [];
8
9
  let closed = false;
10
+ let opened = false;
9
11
  const push = (value) => {
10
12
  if (closed) return;
11
13
  const waiter = waiters.shift();
@@ -18,6 +20,11 @@ function openVoiceSocket(target, Socket = WebSocket) {
18
20
  for (const waiter of waiters) waiter({ value: void 0, done: true });
19
21
  waiters.length = 0;
20
22
  };
23
+ socket.addEventListener("open", () => {
24
+ opened = true;
25
+ const queued = outgoing.splice(0);
26
+ for (const data of queued) socket.send(data);
27
+ });
21
28
  socket.addEventListener("message", (event) => {
22
29
  push(typeof event.data === "string" ? event.data : String(event.data));
23
30
  });
@@ -25,6 +32,11 @@ function openVoiceSocket(target, Socket = WebSocket) {
25
32
  socket.addEventListener("error", end);
26
33
  return {
27
34
  send(data) {
35
+ if (closed) return;
36
+ if (!opened) {
37
+ outgoing.push(data);
38
+ return;
39
+ }
28
40
  socket.send(data);
29
41
  },
30
42
  close() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/openai-realtime/socket.ts"],
4
- "sourcesContent": ["export interface VoiceWireTarget {\n url: string;\n protocols: string[];\n headers: Record<string, string>;\n}\n\nexport interface VoiceWire {\n send(data: string): void;\n close(): void;\n incoming: AsyncIterable<string>;\n}\n\nexport interface VoiceSocket {\n send(data: string): void;\n close(): void;\n addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void;\n addEventListener(type: 'close', listener: () => void): void;\n addEventListener(type: 'error', listener: () => void): void;\n}\n\nexport type VoiceSocketInit = {\n protocols: string[];\n headers: Record<string, string>;\n};\n\nexport type VoiceSocketConstructor = new (url: string, init: VoiceSocketInit) => VoiceSocket;\n\nexport function openVoiceSocket(\n target: VoiceWireTarget,\n Socket: VoiceSocketConstructor = WebSocket as unknown as VoiceSocketConstructor\n): VoiceWire {\n const socket = new Socket(target.url, {\n protocols: target.protocols,\n headers: target.headers,\n });\n const queue: string[] = [];\n const waiters: Array<(result: IteratorResult<string>) => void> = [];\n let closed = false;\n\n const push = (value: string): void => {\n if (closed) return;\n const waiter = waiters.shift();\n if (waiter !== undefined) waiter({ value, done: false });\n else queue.push(value);\n };\n\n const end = (): void => {\n if (closed) return;\n closed = true;\n for (const waiter of waiters) waiter({ value: undefined, done: true });\n waiters.length = 0;\n };\n\n socket.addEventListener('message', (event) => {\n push(typeof event.data === 'string' ? event.data : String(event.data));\n });\n socket.addEventListener('close', end);\n socket.addEventListener('error', end);\n\n return {\n send(data) {\n socket.send(data);\n },\n close() {\n socket.close();\n end();\n },\n incoming: {\n async *[Symbol.asyncIterator]() {\n while (true) {\n const queued = queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (closed) return;\n const next = await new Promise<IteratorResult<string>>((resolve) => {\n waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n },\n },\n };\n}\n"],
5
- "mappings": "AA2BO,SAAS,gBACd,QACA,SAAiC,WACtB;AACX,QAAM,SAAS,IAAI,OAAO,OAAO,KAAK;AAAA,IACpC,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,EAClB,CAAC;AACD,QAAM,QAAkB,CAAC;AACzB,QAAM,UAA2D,CAAC;AAClE,MAAI,SAAS;AAEb,QAAM,OAAO,CAAC,UAAwB;AACpC,QAAI,OAAQ;AACZ,UAAM,SAAS,QAAQ,MAAM;AAC7B,QAAI,WAAW,OAAW,QAAO,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,QAClD,OAAM,KAAK,KAAK;AAAA,EACvB;AAEA,QAAM,MAAM,MAAY;AACtB,QAAI,OAAQ;AACZ,aAAS;AACT,eAAW,UAAU,QAAS,QAAO,EAAE,OAAO,QAAW,MAAM,KAAK,CAAC;AACrE,YAAQ,SAAS;AAAA,EACnB;AAEA,SAAO,iBAAiB,WAAW,CAAC,UAAU;AAC5C,SAAK,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,EACvE,CAAC;AACD,SAAO,iBAAiB,SAAS,GAAG;AACpC,SAAO,iBAAiB,SAAS,GAAG;AAEpC,SAAO;AAAA,IACL,KAAK,MAAM;AACT,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,IACA,QAAQ;AACN,aAAO,MAAM;AACb,UAAI;AAAA,IACN;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,OAAO,aAAa,IAAI;AAC9B,eAAO,MAAM;AACX,gBAAM,SAAS,MAAM,MAAM;AAC3B,cAAI,WAAW,QAAW;AACxB,kBAAM;AACN;AAAA,UACF;AACA,cAAI,OAAQ;AACZ,gBAAM,OAAO,MAAM,IAAI,QAAgC,CAAC,YAAY;AAClE,oBAAQ,KAAK,OAAO;AAAA,UACtB,CAAC;AACD,cAAI,KAAK,SAAS,QAAQ,KAAK,UAAU,OAAW;AACpD,gBAAM,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["export interface VoiceWireTarget {\n url: string;\n protocols: string[];\n headers: Record<string, string>;\n}\n\nexport interface VoiceWire {\n send(data: string): void;\n close(): void;\n incoming: AsyncIterable<string>;\n}\n\nexport interface VoiceSocket {\n send(data: string): void;\n close(): void;\n addEventListener(type: 'open', listener: () => void): void;\n addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void;\n addEventListener(type: 'close', listener: () => void): void;\n addEventListener(type: 'error', listener: () => void): void;\n}\n\nexport type VoiceSocketInit = {\n protocols: string[];\n headers: Record<string, string>;\n};\n\nexport type VoiceSocketConstructor = new (url: string, init: VoiceSocketInit) => VoiceSocket;\n\nexport function openVoiceSocket(\n target: VoiceWireTarget,\n Socket: VoiceSocketConstructor = WebSocket as unknown as VoiceSocketConstructor\n): VoiceWire {\n const socket = new Socket(target.url, {\n protocols: target.protocols,\n headers: target.headers,\n });\n const queue: string[] = [];\n const outgoing: string[] = [];\n const waiters: Array<(result: IteratorResult<string>) => void> = [];\n let closed = false;\n let opened = false;\n\n const push = (value: string): void => {\n if (closed) return;\n const waiter = waiters.shift();\n if (waiter !== undefined) waiter({ value, done: false });\n else queue.push(value);\n };\n\n const end = (): void => {\n if (closed) return;\n closed = true;\n for (const waiter of waiters) waiter({ value: undefined, done: true });\n waiters.length = 0;\n };\n\n socket.addEventListener('open', () => {\n opened = true;\n const queued = outgoing.splice(0);\n for (const data of queued) socket.send(data);\n });\n socket.addEventListener('message', (event) => {\n push(typeof event.data === 'string' ? event.data : String(event.data));\n });\n socket.addEventListener('close', end);\n socket.addEventListener('error', end);\n\n return {\n send(data) {\n if (closed) return;\n if (!opened) {\n outgoing.push(data);\n return;\n }\n socket.send(data);\n },\n close() {\n socket.close();\n end();\n },\n incoming: {\n async *[Symbol.asyncIterator]() {\n while (true) {\n const queued = queue.shift();\n if (queued !== undefined) {\n yield queued;\n continue;\n }\n if (closed) return;\n const next = await new Promise<IteratorResult<string>>((resolve) => {\n waiters.push(resolve);\n });\n if (next.done === true || next.value === undefined) return;\n yield next.value;\n }\n },\n },\n };\n}\n"],
5
+ "mappings": "AA4BO,SAAS,gBACd,QACA,SAAiC,WACtB;AACX,QAAM,SAAS,IAAI,OAAO,OAAO,KAAK;AAAA,IACpC,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,EAClB,CAAC;AACD,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAqB,CAAC;AAC5B,QAAM,UAA2D,CAAC;AAClE,MAAI,SAAS;AACb,MAAI,SAAS;AAEb,QAAM,OAAO,CAAC,UAAwB;AACpC,QAAI,OAAQ;AACZ,UAAM,SAAS,QAAQ,MAAM;AAC7B,QAAI,WAAW,OAAW,QAAO,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,QAClD,OAAM,KAAK,KAAK;AAAA,EACvB;AAEA,QAAM,MAAM,MAAY;AACtB,QAAI,OAAQ;AACZ,aAAS;AACT,eAAW,UAAU,QAAS,QAAO,EAAE,OAAO,QAAW,MAAM,KAAK,CAAC;AACrE,YAAQ,SAAS;AAAA,EACnB;AAEA,SAAO,iBAAiB,QAAQ,MAAM;AACpC,aAAS;AACT,UAAM,SAAS,SAAS,OAAO,CAAC;AAChC,eAAW,QAAQ,OAAQ,QAAO,KAAK,IAAI;AAAA,EAC7C,CAAC;AACD,SAAO,iBAAiB,WAAW,CAAC,UAAU;AAC5C,SAAK,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,EACvE,CAAC;AACD,SAAO,iBAAiB,SAAS,GAAG;AACpC,SAAO,iBAAiB,SAAS,GAAG;AAEpC,SAAO;AAAA,IACL,KAAK,MAAM;AACT,UAAI,OAAQ;AACZ,UAAI,CAAC,QAAQ;AACX,iBAAS,KAAK,IAAI;AAClB;AAAA,MACF;AACA,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,IACA,QAAQ;AACN,aAAO,MAAM;AACb,UAAI;AAAA,IACN;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,OAAO,aAAa,IAAI;AAC9B,eAAO,MAAM;AACX,gBAAM,SAAS,MAAM,MAAM;AAC3B,cAAI,WAAW,QAAW;AACxB,kBAAM;AACN;AAAA,UACF;AACA,cAAI,OAAQ;AACZ,gBAAM,OAAO,MAAM,IAAI,QAAgC,CAAC,YAAY;AAClE,oBAAQ,KAAK,OAAO;AAAA,UACtB,CAAC;AACD,cAAI,KAAK,SAAS,QAAQ,KAAK,UAAU,OAAW;AACpD,gBAAM,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }