@ai-ecoverse/pi-audio 1.18.0 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,7 +52,7 @@ pi-audio does not apply that patch. Hosts such as slicc-agent record monkeypatch
52
52
 
53
53
  `@ai-ecoverse/pi-audio/elevenlabs-agents` registers `elevenlabs-agent`, a hosted cascade (`provisioning: "agent"`, `truncation: "server"`, `liveToolUpdate: false`). `provisionAgent` creates or updates the agent and reuses it when the spec hash matches. pi tools become client tools. A string `llm` is a vendor model name. A `CustomLlm` sets `llm` to `custom-llm` and sends `custom_llm.url`, `model_id`, `api_type`, and `request_headers` (ElevenLabs `api_key` is a workspace secret reference, not a raw token). `connectRealtime` uses a `RealtimeToken` from `mintRealtimeToken` (`get-signed-url`) when one is passed. Otherwise an API key provisions the agent and mints the signed URL. Silent `inject` sends `contextual_update` with `context_id` `pi-audio`. `close()` prices `audio.inputSeconds` from the wall-clock session at $0.08 per minute.
54
54
 
55
- `talkerTools` is `ask_agent` and `read`. pi-audio defines `askAgent` (a pi `Tool` whose parameters are `request` and optional `context`) and the `Thinker` interface. The host implements `Thinker` and supplies `read`. `bindThinker` runs direct tools with `directTimeoutMs` and truncates their text to `directMaxChars`. On a blocking `ask_agent` it answers with `ack` immediately and injects the result later. On an async call it `respond`s with the result and `options.result`. Progress is injected with `options.progress`. `tool_call_cancelled` aborts the thinker. The binding also yields `thinker_progress` / `thinker_result` captions and `thinking` / `tool` / `idle` phases.
55
+ `talkerTools` is `ask_agent` and `read`. pi-audio defines `askAgent` (a pi `Tool` whose parameters are `request` and optional `context`) and the `Thinker` interface. The host implements `Thinker` and supplies `read`. `bindThinker` runs direct tools with `directTimeoutMs` and truncates their text to `directMaxChars`. On a blocking `ask_agent` it answers with `ack` immediately and injects the result later. On an async call it `respond`s with the result and `options.result`. Progress is injected with `options.progress`. `tool_call_cancelled` aborts the thinker and any direct tool. A blocking failure is injected after the acknowledgement, with a `thinker_result` caption. `Thinker.ask` receives the optional `context` string. Closing the session aborts work that is still running. The binding also yields `thinker_progress` / `thinker_result` captions and `thinking` / `tool` / `idle` phases.
56
56
 
57
57
  ## Development
58
58
 
@@ -205,6 +205,8 @@ class GeminiSession {
205
205
  #closed = false;
206
206
  #ended = false;
207
207
  #meters;
208
+ #toolGroups = /* @__PURE__ */ new Map();
209
+ #toolGroupOf = /* @__PURE__ */ new Map();
208
210
  constructor(model, context, vocabulary, open, target, wire) {
209
211
  this.model = model;
210
212
  this.#context = context;
@@ -285,20 +287,14 @@ class GeminiSession {
285
287
  this.#assertOpen();
286
288
  const send = () => {
287
289
  const mode = delivery === void 0 ? "interrupt" : delivery;
288
- this.#send({
289
- toolResponse: {
290
- functionResponses: [
291
- {
292
- id: callId,
293
- name: result.toolName,
294
- scheduling: scheduling[mode],
295
- response: {
296
- status: result.isError ? "error" : "ok",
297
- retryable: false,
298
- message: resultText(result)
299
- }
300
- }
301
- ]
290
+ this.#queueTool(callId, {
291
+ id: callId,
292
+ name: result.toolName,
293
+ scheduling: scheduling[mode],
294
+ response: {
295
+ status: result.isError ? "error" : "ok",
296
+ retryable: false,
297
+ message: resultText(result)
302
298
  }
303
299
  });
304
300
  };
@@ -440,23 +436,44 @@ class GeminiSession {
440
436
  next.send(JSON.stringify({ setup: this.#setup(handle) }));
441
437
  return next;
442
438
  }
439
+ #queueTool(callId, response) {
440
+ const groupId = this.#toolGroupOf.get(callId) ?? "";
441
+ const group = groupId.length === 0 ? void 0 : this.#toolGroups.get(groupId);
442
+ if (group === void 0) {
443
+ this.#send({ toolResponse: { functionResponses: [response] } });
444
+ return;
445
+ }
446
+ group.responses.push(response);
447
+ group.pending.delete(callId);
448
+ this.#toolGroupOf.delete(callId);
449
+ if (group.pending.size > 0) return;
450
+ this.#toolGroups.delete(groupId);
451
+ this.#send({ toolResponse: { functionResponses: group.responses } });
452
+ }
443
453
  #onToolCall(value) {
444
454
  const call = record(value);
445
455
  if (call === void 0 || !Array.isArray(call.functionCalls)) return;
456
+ const ids = [];
446
457
  for (const entry of call.functionCalls) {
447
458
  const fn = record(entry);
448
459
  if (fn === void 0) continue;
460
+ const id = text(fn.id);
461
+ if (id.length > 0) ids.push(id);
449
462
  this.#push({
450
463
  type: "tool_call",
451
464
  blocking: this.model.toolCalls === "blocking",
452
465
  call: {
453
466
  type: "toolCall",
454
- id: text(fn.id),
467
+ id,
455
468
  name: text(fn.name),
456
469
  arguments: argumentsOf(fn.args)
457
470
  }
458
471
  });
459
472
  }
473
+ const groupId = ids.at(0);
474
+ if (groupId === void 0) return;
475
+ this.#toolGroups.set(groupId, { pending: new Set(ids), responses: [] });
476
+ for (const id of ids) this.#toolGroupOf.set(id, groupId);
460
477
  }
461
478
  #onCancel(value) {
462
479
  const cancellation = record(value);
@@ -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
  }
package/dist/talker.js CHANGED
@@ -88,6 +88,16 @@ function capResult(result, maxChars) {
88
88
  });
89
89
  return { ...result, content };
90
90
  }
91
+ function requestContext(call) {
92
+ const value = call.arguments.context;
93
+ return typeof value === "string" ? value : void 0;
94
+ }
95
+ function track(pending, promise) {
96
+ pending.add(promise);
97
+ void promise.finally(() => {
98
+ pending.delete(promise);
99
+ });
100
+ }
91
101
  function bindThinker(session, thinker, direct, options) {
92
102
  const captions = new PushStream();
93
103
  const phases = new PushStream();
@@ -102,42 +112,31 @@ function bindThinker(session, thinker, direct, options) {
102
112
  async function run(session, thinker, direct, options, captions, phases) {
103
113
  const heard = [];
104
114
  const running = /* @__PURE__ */ new Map();
115
+ const pending = /* @__PURE__ */ new Set();
105
116
  for await (const event of session) {
106
117
  if (event.type === "input_transcript" && event.final) heard.push(`user: ${event.text}`);
107
118
  if (event.type === "output_transcript" && event.final) heard.push(`talker: ${event.text}`);
108
119
  if (event.type === "tool_call_cancelled") {
109
- const job2 = running.get(event.callId);
110
- if (!job2) continue;
111
- job2.abort.abort();
112
- if (!job2.answered) {
113
- job2.answered = true;
114
- send(() => session.respond(event.callId, errorResult(job2.call, new Error("cancelled"))));
115
- }
120
+ cancelJob(session, running, event.callId);
116
121
  continue;
117
122
  }
118
123
  if (event.type !== "tool_call") continue;
119
124
  const call = event.call;
120
125
  if (call.name !== askAgent.name) {
121
- phases.push({
122
- phase: "tool",
123
- toolName: call.name,
124
- toolCallId: call.id,
125
- args: call.arguments
126
- });
127
- void direct(call, AbortSignal.timeout(options.directTimeoutMs)).then((result) => {
128
- send(() => session.respond(call.id, capResult(result, options.directMaxChars)));
129
- phases.push({ phase: "idle" });
130
- }).catch((error) => {
131
- send(() => session.respond(call.id, errorResult(call, error)));
132
- phases.push({ phase: "idle" });
133
- });
126
+ track(pending, runDirect(session, direct, options, phases, running, call));
134
127
  continue;
135
128
  }
136
- const job = { abort: new AbortController(), answered: event.blocking, call };
129
+ const job = {
130
+ abort: new AbortController(),
131
+ answered: false,
132
+ dropped: false,
133
+ call,
134
+ kind: "ask"
135
+ };
137
136
  running.set(call.id, job);
138
137
  phases.push({ phase: "thinking" });
139
138
  if (event.blocking) send(() => session.respond(call.id, acknowledged(call, options.ack)));
140
- void followAsk(
139
+ const ask = followAsk(
141
140
  session,
142
141
  thinker,
143
142
  options,
@@ -149,14 +148,52 @@ async function run(session, thinker, direct, options, captions, phases) {
149
148
  event.blocking,
150
149
  job
151
150
  );
151
+ track(pending, ask);
152
152
  }
153
+ for (const job of running.values()) {
154
+ job.dropped = true;
155
+ job.abort.abort();
156
+ }
157
+ await Promise.allSettled([...pending]);
158
+ }
159
+ function cancelJob(session, running, callId) {
160
+ const job = running.get(callId);
161
+ if (!job) return;
162
+ job.abort.abort();
163
+ job.dropped = true;
164
+ if (job.kind !== "ask" || job.answered) return;
165
+ job.answered = true;
166
+ send(() => session.respond(callId, errorResult(job.call, new Error("cancelled"))));
167
+ }
168
+ function runDirect(session, direct, options, phases, running, call) {
169
+ const abort = new AbortController();
170
+ const signal = AbortSignal.any([abort.signal, AbortSignal.timeout(options.directTimeoutMs)]);
171
+ const job = { abort, answered: false, dropped: false, call, kind: "direct" };
172
+ running.set(call.id, job);
173
+ phases.push({
174
+ phase: "tool",
175
+ toolName: call.name,
176
+ toolCallId: call.id,
177
+ args: call.arguments
178
+ });
179
+ return direct(call, signal).then((result) => {
180
+ if (job.dropped) return;
181
+ send(() => session.respond(call.id, capResult(result, options.directMaxChars)));
182
+ }).catch((error) => {
183
+ if (job.dropped) return;
184
+ send(() => session.respond(call.id, errorResult(call, error)));
185
+ }).finally(() => {
186
+ running.delete(call.id);
187
+ phases.push({ phase: "idle" });
188
+ });
153
189
  }
154
190
  async function followAsk(session, thinker, options, captions, phases, running, heard, call, blocking, job) {
155
191
  try {
156
192
  for await (const update of thinker.ask(
157
193
  String(call.arguments.request),
158
194
  heard.slice(-heardLimit),
159
- job.abort.signal
195
+ job.abort.signal,
196
+ requestContext(call)
160
197
  )) {
161
198
  if (job.abort.signal.aborted) break;
162
199
  if (update.type === "progress") {
@@ -175,10 +212,17 @@ async function followAsk(session, thinker, options, captions, phases, running, h
175
212
  else send(() => session.respond(call.id, update.message, options.result));
176
213
  }
177
214
  } catch (error) {
178
- if (!job.abort.signal.aborted && !job.answered) {
179
- job.answered = true;
180
- send(() => session.respond(call.id, errorResult(call, error)));
181
- }
215
+ if (job.abort.signal.aborted || job.answered) return;
216
+ job.answered = true;
217
+ const message = errorResult(call, error);
218
+ captions.push({
219
+ type: "thinker_result",
220
+ callId: call.id,
221
+ summary: textOf(message),
222
+ isError: true
223
+ });
224
+ if (blocking) send(() => session.inject(textOf(message), options.result));
225
+ else send(() => session.respond(call.id, message));
182
226
  } finally {
183
227
  running.delete(call.id);
184
228
  phases.push({ phase: "idle" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/talker.ts"],
4
- "sourcesContent": ["import {\n type JsonObject,\n type Tool,\n type ToolCall,\n type ToolResultMessage,\n Type,\n} from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { Delivery } from './models.ts';\nimport type { RealtimeSession } from './realtime.ts';\n\nconst heardLimit = 20;\n\nexport const talkerTools = ['ask_agent', 'read'] as const;\n\nexport const askAgent: Tool = {\n name: 'ask_agent',\n description:\n 'Hand a request to the coding agent. Use for anything that needs files, the browser, tools or more than a few seconds of thought.',\n parameters: Type.Object({\n request: Type.String(),\n context: Type.Optional(Type.String()),\n }),\n};\n\nexport type ThinkerUpdate =\n | { type: 'progress'; text: string }\n | { type: 'result'; message: ToolResultMessage };\n\nexport interface Thinker {\n ask(request: string, heard: string[], signal: AbortSignal): AsyncIterable<ThinkerUpdate>;\n}\n\nexport interface BindThinkerOptions {\n progress: Delivery;\n result: Delivery;\n ack: string;\n directTimeoutMs: number;\n directMaxChars: number;\n}\n\nexport type ThinkerCaption =\n | { type: 'thinker_progress'; callId: string; text: string }\n | { type: 'thinker_result'; callId: string; summary: string; isError: boolean };\n\nexport type ThinkerPhase =\n | { phase: 'thinking' }\n | { phase: 'tool'; toolName: string; toolCallId: string; args: JsonObject }\n | { phase: 'idle' };\n\nexport interface BoundThinker {\n captions: AsyncIterable<ThinkerCaption>;\n phases: AsyncIterable<ThinkerPhase>;\n done: Promise<void>;\n}\n\nclass PushStream<T> implements AsyncIterable<T> {\n readonly #queue: T[] = [];\n readonly #waiters: Array<(result: { value?: T; done: boolean }) => void> = [];\n #closed = false;\n\n push(value: T): void {\n if (this.#closed) return;\n const waiter = this.#waiters.shift();\n if (waiter) waiter({ value, done: false });\n else this.#queue.push(value);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\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<{ value?: T; done: boolean }>((resolve) =>\n this.#waiters.push(resolve)\n );\n if (next.done || next.value === undefined) return;\n yield next.value;\n }\n }\n}\n\nfunction send(action: () => void): void {\n try {\n action();\n } catch (error) {\n if (!(error instanceof VoiceRequestError)) throw error;\n }\n}\n\nfunction textOf(message: ToolResultMessage): string {\n return message.content\n .filter((part) => part.type === 'text')\n .map((part) => part.text)\n .join('\\n');\n}\n\nfunction acknowledged(call: ToolCall, ack: string): ToolResultMessage {\n return {\n role: 'toolResult',\n toolCallId: call.id,\n toolName: call.name,\n content: [{ type: 'text', text: ack }],\n isError: false,\n timestamp: Date.now(),\n };\n}\n\nfunction errorResult(call: ToolCall, error: unknown): ToolResultMessage {\n return {\n role: 'toolResult',\n toolCallId: call.id,\n toolName: call.name,\n content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],\n isError: true,\n timestamp: Date.now(),\n };\n}\n\nfunction capResult(result: ToolResultMessage, maxChars: number): ToolResultMessage {\n let left = maxChars;\n const content = result.content.map((part) => {\n if (part.type !== 'text') return part;\n if (left <= 0) return { ...part, text: '' };\n if (part.text.length <= left) {\n left -= part.text.length;\n return part;\n }\n const text = part.text.slice(0, left);\n left = 0;\n return { ...part, text };\n });\n return { ...result, content };\n}\n\ninterface AskJob {\n abort: AbortController;\n answered: boolean;\n call: ToolCall;\n}\n\nexport function bindThinker(\n session: RealtimeSession,\n thinker: Thinker,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions\n): BoundThinker {\n const captions = new PushStream<ThinkerCaption>();\n const phases = new PushStream<ThinkerPhase>();\n const done = run(session, thinker, direct, options, captions, phases).finally(() => {\n captions.close();\n phases.close();\n captions.close();\n phases.close();\n });\n return { captions, phases, done };\n}\n\nasync function run(\n session: RealtimeSession,\n thinker: Thinker,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions,\n captions: PushStream<ThinkerCaption>,\n phases: PushStream<ThinkerPhase>\n): Promise<void> {\n const heard: string[] = [];\n const running = new Map<string, AskJob>();\n for await (const event of session) {\n if (event.type === 'input_transcript' && event.final) heard.push(`user: ${event.text}`);\n if (event.type === 'output_transcript' && event.final) heard.push(`talker: ${event.text}`);\n if (event.type === 'tool_call_cancelled') {\n const job = running.get(event.callId);\n if (!job) continue;\n job.abort.abort();\n if (!job.answered) {\n job.answered = true;\n send(() => session.respond(event.callId, errorResult(job.call, new Error('cancelled'))));\n }\n continue;\n }\n if (event.type !== 'tool_call') continue;\n const call = event.call;\n if (call.name !== askAgent.name) {\n phases.push({\n phase: 'tool',\n toolName: call.name,\n toolCallId: call.id,\n args: call.arguments,\n });\n void direct(call, AbortSignal.timeout(options.directTimeoutMs))\n .then((result) => {\n send(() => session.respond(call.id, capResult(result, options.directMaxChars)));\n phases.push({ phase: 'idle' });\n })\n .catch((error: unknown) => {\n send(() => session.respond(call.id, errorResult(call, error)));\n phases.push({ phase: 'idle' });\n });\n continue;\n }\n const job: AskJob = { abort: new AbortController(), answered: event.blocking, call };\n running.set(call.id, job);\n phases.push({ phase: 'thinking' });\n if (event.blocking) send(() => session.respond(call.id, acknowledged(call, options.ack)));\n void followAsk(\n session,\n thinker,\n options,\n captions,\n phases,\n running,\n heard,\n call,\n event.blocking,\n job\n );\n }\n}\n\nasync function followAsk(\n session: RealtimeSession,\n thinker: Thinker,\n options: BindThinkerOptions,\n captions: PushStream<ThinkerCaption>,\n phases: PushStream<ThinkerPhase>,\n running: Map<string, AskJob>,\n heard: string[],\n call: ToolCall,\n blocking: boolean,\n job: AskJob\n): Promise<void> {\n try {\n for await (const update of thinker.ask(\n String(call.arguments.request),\n heard.slice(-heardLimit),\n job.abort.signal\n )) {\n if (job.abort.signal.aborted) break;\n if (update.type === 'progress') {\n captions.push({ type: 'thinker_progress', callId: call.id, text: update.text });\n send(() => session.inject(update.text, options.progress));\n continue;\n }\n job.answered = true;\n captions.push({\n type: 'thinker_result',\n callId: call.id,\n summary: textOf(update.message),\n isError: update.message.isError,\n });\n if (blocking) send(() => session.inject(textOf(update.message), options.result));\n else send(() => session.respond(call.id, update.message, options.result));\n }\n } catch (error) {\n if (!job.abort.signal.aborted && !job.answered) {\n job.answered = true;\n send(() => session.respond(call.id, errorResult(call, error)));\n }\n } finally {\n running.delete(call.id);\n phases.push({ phase: 'idle' });\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["job"]
4
+ "sourcesContent": ["import {\n type JsonObject,\n type Tool,\n type ToolCall,\n type ToolResultMessage,\n Type,\n} from '@earendil-works/pi-ai';\nimport { VoiceRequestError } from './errors.ts';\nimport type { Delivery } from './models.ts';\nimport type { RealtimeSession } from './realtime.ts';\n\nconst heardLimit = 20;\n\nexport const talkerTools = ['ask_agent', 'read'] as const;\n\nexport const askAgent: Tool = {\n name: 'ask_agent',\n description:\n 'Hand a request to the coding agent. Use for anything that needs files, the browser, tools or more than a few seconds of thought.',\n parameters: Type.Object({\n request: Type.String(),\n context: Type.Optional(Type.String()),\n }),\n};\n\nexport type ThinkerUpdate =\n | { type: 'progress'; text: string }\n | { type: 'result'; message: ToolResultMessage };\n\nexport interface Thinker {\n ask(\n request: string,\n heard: string[],\n signal: AbortSignal,\n context?: string\n ): AsyncIterable<ThinkerUpdate>;\n}\n\nexport interface BindThinkerOptions {\n progress: Delivery;\n result: Delivery;\n ack: string;\n directTimeoutMs: number;\n directMaxChars: number;\n}\n\nexport type ThinkerCaption =\n | { type: 'thinker_progress'; callId: string; text: string }\n | { type: 'thinker_result'; callId: string; summary: string; isError: boolean };\n\nexport type ThinkerPhase =\n | { phase: 'thinking' }\n | { phase: 'tool'; toolName: string; toolCallId: string; args: JsonObject }\n | { phase: 'idle' };\n\nexport interface BoundThinker {\n captions: AsyncIterable<ThinkerCaption>;\n phases: AsyncIterable<ThinkerPhase>;\n done: Promise<void>;\n}\n\nclass PushStream<T> implements AsyncIterable<T> {\n readonly #queue: T[] = [];\n readonly #waiters: Array<(result: { value?: T; done: boolean }) => void> = [];\n #closed = false;\n\n push(value: T): void {\n if (this.#closed) return;\n const waiter = this.#waiters.shift();\n if (waiter) waiter({ value, done: false });\n else this.#queue.push(value);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n while (this.#waiters.length > 0) this.#waiters.shift()?.({ done: true });\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\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<{ value?: T; done: boolean }>((resolve) =>\n this.#waiters.push(resolve)\n );\n if (next.done || next.value === undefined) return;\n yield next.value;\n }\n }\n}\n\nfunction send(action: () => void): void {\n try {\n action();\n } catch (error) {\n if (!(error instanceof VoiceRequestError)) throw error;\n }\n}\n\nfunction textOf(message: ToolResultMessage): string {\n return message.content\n .filter((part) => part.type === 'text')\n .map((part) => part.text)\n .join('\\n');\n}\n\nfunction acknowledged(call: ToolCall, ack: string): ToolResultMessage {\n return {\n role: 'toolResult',\n toolCallId: call.id,\n toolName: call.name,\n content: [{ type: 'text', text: ack }],\n isError: false,\n timestamp: Date.now(),\n };\n}\n\nfunction errorResult(call: ToolCall, error: unknown): ToolResultMessage {\n return {\n role: 'toolResult',\n toolCallId: call.id,\n toolName: call.name,\n content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],\n isError: true,\n timestamp: Date.now(),\n };\n}\n\nfunction capResult(result: ToolResultMessage, maxChars: number): ToolResultMessage {\n let left = maxChars;\n const content = result.content.map((part) => {\n if (part.type !== 'text') return part;\n if (left <= 0) return { ...part, text: '' };\n if (part.text.length <= left) {\n left -= part.text.length;\n return part;\n }\n const text = part.text.slice(0, left);\n left = 0;\n return { ...part, text };\n });\n return { ...result, content };\n}\n\ninterface AskJob {\n abort: AbortController;\n answered: boolean;\n dropped: boolean;\n call: ToolCall;\n kind: 'ask' | 'direct';\n}\n\nfunction requestContext(call: ToolCall): string | undefined {\n const value = call.arguments.context;\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction track(pending: Set<Promise<void>>, promise: Promise<void>): void {\n pending.add(promise);\n void promise.finally(() => {\n pending.delete(promise);\n });\n}\n\nexport function bindThinker(\n session: RealtimeSession,\n thinker: Thinker,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions\n): BoundThinker {\n const captions = new PushStream<ThinkerCaption>();\n const phases = new PushStream<ThinkerPhase>();\n const done = run(session, thinker, direct, options, captions, phases).finally(() => {\n captions.close();\n phases.close();\n captions.close();\n phases.close();\n });\n return { captions, phases, done };\n}\n\nasync function run(\n session: RealtimeSession,\n thinker: Thinker,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions,\n captions: PushStream<ThinkerCaption>,\n phases: PushStream<ThinkerPhase>\n): Promise<void> {\n const heard: string[] = [];\n const running = new Map<string, AskJob>();\n const pending = new Set<Promise<void>>();\n for await (const event of session) {\n if (event.type === 'input_transcript' && event.final) heard.push(`user: ${event.text}`);\n if (event.type === 'output_transcript' && event.final) heard.push(`talker: ${event.text}`);\n if (event.type === 'tool_call_cancelled') {\n cancelJob(session, running, event.callId);\n continue;\n }\n if (event.type !== 'tool_call') continue;\n const call = event.call;\n if (call.name !== askAgent.name) {\n track(pending, runDirect(session, direct, options, phases, running, call));\n continue;\n }\n const job: AskJob = {\n abort: new AbortController(),\n answered: false,\n dropped: false,\n call,\n kind: 'ask',\n };\n running.set(call.id, job);\n phases.push({ phase: 'thinking' });\n if (event.blocking) send(() => session.respond(call.id, acknowledged(call, options.ack)));\n const ask = followAsk(\n session,\n thinker,\n options,\n captions,\n phases,\n running,\n heard,\n call,\n event.blocking,\n job\n );\n track(pending, ask);\n }\n for (const job of running.values()) {\n job.dropped = true;\n job.abort.abort();\n }\n await Promise.allSettled([...pending]);\n}\n\nfunction cancelJob(session: RealtimeSession, running: Map<string, AskJob>, callId: string): void {\n const job = running.get(callId);\n if (!job) return;\n job.abort.abort();\n job.dropped = true;\n if (job.kind !== 'ask' || job.answered) return;\n job.answered = true;\n send(() => session.respond(callId, errorResult(job.call, new Error('cancelled'))));\n}\n\nfunction runDirect(\n session: RealtimeSession,\n direct: (call: ToolCall, signal: AbortSignal) => Promise<ToolResultMessage>,\n options: BindThinkerOptions,\n phases: PushStream<ThinkerPhase>,\n running: Map<string, AskJob>,\n call: ToolCall\n): Promise<void> {\n const abort = new AbortController();\n const signal = AbortSignal.any([abort.signal, AbortSignal.timeout(options.directTimeoutMs)]);\n const job: AskJob = { abort, answered: false, dropped: false, call, kind: 'direct' };\n running.set(call.id, job);\n phases.push({\n phase: 'tool',\n toolName: call.name,\n toolCallId: call.id,\n args: call.arguments,\n });\n return direct(call, signal)\n .then((result) => {\n if (job.dropped) return;\n send(() => session.respond(call.id, capResult(result, options.directMaxChars)));\n })\n .catch((error: unknown) => {\n if (job.dropped) return;\n send(() => session.respond(call.id, errorResult(call, error)));\n })\n .finally(() => {\n running.delete(call.id);\n phases.push({ phase: 'idle' });\n });\n}\n\nasync function followAsk(\n session: RealtimeSession,\n thinker: Thinker,\n options: BindThinkerOptions,\n captions: PushStream<ThinkerCaption>,\n phases: PushStream<ThinkerPhase>,\n running: Map<string, AskJob>,\n heard: string[],\n call: ToolCall,\n blocking: boolean,\n job: AskJob\n): Promise<void> {\n try {\n for await (const update of thinker.ask(\n String(call.arguments.request),\n heard.slice(-heardLimit),\n job.abort.signal,\n requestContext(call)\n )) {\n if (job.abort.signal.aborted) break;\n if (update.type === 'progress') {\n captions.push({ type: 'thinker_progress', callId: call.id, text: update.text });\n send(() => session.inject(update.text, options.progress));\n continue;\n }\n job.answered = true;\n captions.push({\n type: 'thinker_result',\n callId: call.id,\n summary: textOf(update.message),\n isError: update.message.isError,\n });\n if (blocking) send(() => session.inject(textOf(update.message), options.result));\n else send(() => session.respond(call.id, update.message, options.result));\n }\n } catch (error) {\n if (job.abort.signal.aborted || job.answered) return;\n job.answered = true;\n const message = errorResult(call, error);\n captions.push({\n type: 'thinker_result',\n callId: call.id,\n summary: textOf(message),\n isError: true,\n });\n if (blocking) send(() => session.inject(textOf(message), options.result));\n else send(() => session.respond(call.id, message));\n } finally {\n running.delete(call.id);\n phases.push({ phase: 'idle' });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -11,6 +11,7 @@ export interface VoiceWire {
11
11
  export interface VoiceSocket {
12
12
  send(data: string): void;
13
13
  close(): void;
14
+ addEventListener(type: 'open', listener: () => void): void;
14
15
  addEventListener(type: 'message', listener: (event: {
15
16
  data: unknown;
16
17
  }) => void): void;
@@ -11,7 +11,7 @@ export type ThinkerUpdate = {
11
11
  message: ToolResultMessage;
12
12
  };
13
13
  export interface Thinker {
14
- ask(request: string, heard: string[], signal: AbortSignal): AsyncIterable<ThinkerUpdate>;
14
+ ask(request: string, heard: string[], signal: AbortSignal, context?: string): AsyncIterable<ThinkerUpdate>;
15
15
  }
16
16
  export interface BindThinkerOptions {
17
17
  progress: Delivery;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/pi-audio",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "description": "Voice bindings for pi: speech-to-text, text-to-speech and realtime speech for pi-coding-agent plugins and slicc-agent",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",