{"version":3,"file":"remote-z8wjPDtk.cjs","names":["ReadableStream","VOICE_TEXT_FLUSH","RequestContext","llm","voice","VOICE_TURN_METADATA","FlushSentinel","createGenerationMetrics","voice","FlushSentinel","VOICE_TURN_METADATA","RequestContext","ReadableStream","APIStatusError","APIConnectionError","VOICE_TEXT_FLUSH","APITimeoutError","APIError"],"sources":["../src/messages.ts","../src/voice-stream.ts","../src/bridge.ts","../src/speech.ts","../src/remote.ts"],"sourcesContent":["import type { llm } from '@livekit/agents';\n\n/**\n * Fixed id LiveKit gives the customer Agent's instructions when it injects them as a leading\n * `role: 'system'` message into the chat context passed to `chat()` / `llmNode`. We drop this\n * item so the server-side Mastra agent's own system prompt is authoritative.\n */\nexport const LIVEKIT_INSTRUCTIONS_MESSAGE_ID = 'lk.agent_task.instructions';\n\n/**\n * A message bound for `agent.stream(...)` (in-process) or the Mastra server stream route (remote).\n * `id` carries the LiveKit `ChatMessage.id` so the server can dedupe/upsert by id — making\n * base-class retries, preemptive double-sends, and the interrupted-turn reconciliation recipe idempotent.\n */\nexport type VoiceTurnMessage =\n  | { role: 'system'; content: string; id?: string }\n  | { role: 'user'; content: string; id?: string }\n  | { role: 'assistant'; content: string; id?: string };\n\nfunction textOfMessage(message: llm.ChatMessage): string {\n  const parts: string[] = [];\n  for (const part of message.content) {\n    if (typeof part === 'string') {\n      parts.push(part);\n    } else if (part.type === 'instructions') {\n      parts.push(part.value);\n    } else if (part.type === 'audio_content' && part.transcript) {\n      parts.push(part.transcript);\n    }\n  }\n  return parts.join('\\n').trim();\n}\n\nfunction toVoiceTurnMessage(item: llm.ChatItem): VoiceTurnMessage | undefined {\n  if (item.type !== 'message') return undefined;\n  const content = textOfMessage(item);\n  if (!content) return undefined;\n  const id = item.id;\n  if (item.role === 'user') return { role: 'user', content, id };\n  if (item.role === 'assistant') return { role: 'assistant', content, id };\n  // 'system' and 'developer' both map to a Mastra system message.\n  return { role: 'system', content, id };\n}\n\n/**\n * Extracts only the messages added since the agent last spoke. Used when Mastra Memory is\n * the source of truth for conversation history: prior turns are already persisted in the\n * thread, so re-sending them would duplicate history.\n *\n * Two extensions over the naive \"slice after the last assistant message\":\n *\n * - **Interrupted-turn self-heal:** when the last assistant message was cut off by barge-in\n *   (`interrupted: true`), the server never persisted it — aborted runs skip persistence — so\n *   its heard-only text is missing from the thread. Re-send that fragment (ordered first) this\n *   turn to backfill it. It stops being \"the last assistant message\" once a full reply lands,\n *   so each interrupted fragment is sent exactly once, on the following turn.\n * - **Instructions filter:** LiveKit injects the customer Agent's `instructions` as a\n *   leading `system` message ({@link LIVEKIT_INSTRUCTIONS_MESSAGE_ID}); the server-side Mastra\n *   agent owns its own system prompt, so drop it (it would otherwise ship on the first turn,\n *   before any assistant message).\n */\nexport function extractNewTurnMessages(chatCtx: llm.ChatContext): VoiceTurnMessage[] {\n  const items = chatCtx.items;\n  let lastAssistantIdx = -1;\n  for (let i = items.length - 1; i >= 0; i--) {\n    const item = items[i];\n    if (item?.type === 'message' && item.role === 'assistant') {\n      lastAssistantIdx = i;\n      break;\n    }\n  }\n  const lastAssistant = lastAssistantIdx >= 0 ? items[lastAssistantIdx] : undefined;\n  const healInterrupted =\n    lastAssistant?.type === 'message' && lastAssistant.role === 'assistant' && lastAssistant.interrupted;\n  // Include the interrupted fragment by starting the slice AT it, otherwise start strictly after.\n  const startIdx = healInterrupted ? lastAssistantIdx : lastAssistantIdx + 1;\n\n  const messages: VoiceTurnMessage[] = [];\n  for (const item of items.slice(startIdx)) {\n    if (item.type === 'message' && item.id === LIVEKIT_INSTRUCTIONS_MESSAGE_ID) continue;\n    const message = toVoiceTurnMessage(item);\n    if (message) messages.push(message);\n  }\n  return messages;\n}\n\n/**\n * Converts the full LiveKit chat context to Mastra messages. Used when the bridge runs\n * without Mastra Memory and LiveKit's in-session context is the only history. The agent's\n * LiveKit-level instructions are excluded — the Mastra agent applies its own instructions.\n */\nexport function chatContextToMessages(chatCtx: llm.ChatContext): VoiceTurnMessage[] {\n  const withoutInstructions = chatCtx.copy({ excludeInstructions: true, excludeFunctionCall: true });\n  const messages: VoiceTurnMessage[] = [];\n  for (const item of withoutInstructions.items) {\n    const message = toVoiceTurnMessage(item);\n    if (message) messages.push(message);\n  }\n  return messages;\n}\n","/** A one-to-one mapping that awaits upstream cancellation, including pending reads. */\nexport function mapVoiceStream<Input, Output>(\n  source: ReadableStream<Input>,\n  map: (chunk: Input) => Output,\n): ReadableStream<Output> {\n  const reader = source.getReader();\n  let cancelled = false;\n  return new ReadableStream<Output>({\n    async pull(controller) {\n      try {\n        const { done, value } = await reader.read();\n        if (cancelled) return;\n        if (done) controller.close();\n        else controller.enqueue(map(value));\n      } catch (error) {\n        if (!cancelled) controller.error(error);\n      }\n    },\n    cancel(reason) {\n      cancelled = true;\n      return reader.cancel(reason);\n    },\n  });\n}\n","import { ReadableStream } from 'node:stream/web';\nimport { FlushSentinel, llm, voice } from '@livekit/agents';\nimport type { Agent as MastraAgent, AgentExecutionOptionsBase, AgentMemoryOption } from '@mastra/core/agent';\nimport type { TracingContext } from '@mastra/core/observability';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { chatContextToMessages, extractNewTurnMessages } from './messages';\nimport type { VoiceTurnMessage } from './messages';\nimport { createGenerationMetrics, VOICE_TEXT_FLUSH, VOICE_TURN_METADATA } from './turn-metrics';\nimport type { VoiceReplyChunk, VoiceTurnMetricsHook } from './turn-metrics';\nimport { mapVoiceStream } from './voice-stream';\n\nconst DEFAULT_INSTRUCTIONS = 'You are a helpful voice assistant powered by a Mastra agent.';\n\n/** Default spoken text for periodic AI re-disclosure. See {@link MastraVoiceAgentOptions.greetingReminder}. */\nexport const DEFAULT_DISCLOSURE_REMINDER = \"Just a reminder, you're speaking with an AI assistant.\";\n\n/**\n * Tracks periodic AI re-disclosure for a single call. `due()` returns the reminder text once\n * `everyMs` has elapsed since the last disclosure (resetting the clock), otherwise `undefined`.\n * Time is injectable so the interval logic is deterministically testable.\n */\nexport class DisclosureReminder {\n  private lastAt: number;\n  constructor(\n    private readonly everyMs: number,\n    private readonly text: string,\n    now: number = Date.now(),\n  ) {\n    this.lastAt = now;\n  }\n  /** Call once per turn: the reminder text if it's due, else `undefined`. Does not reset the clock —\n   * call {@link DisclosureReminder.markDelivered} once the reminder is actually threaded into the\n   * outgoing reply, so a reminder that never makes it out isn't silently skipped for a full interval. */\n  due(now: number = Date.now()): string | undefined {\n    if (now - this.lastAt < this.everyMs) return undefined;\n    return this.text;\n  }\n  /** Resets the clock. Call only once the reminder text from {@link due} was actually emitted. */\n  markDelivered(now: number = Date.now()): void {\n    this.lastAt = now;\n  }\n}\n\n/**\n * Wraps `source` in a stream that emits `text` as a single leading chunk (with a trailing space, so\n * TTS pauses before the reply) before piping the rest of `source` through unchanged. Cancelling the\n * wrapper cancels `source` — so barge-in still aborts the underlying generation.\n */\nexport function prependText<T>(source: ReadableStream<T>, text: string): ReadableStream<T | string> {\n  const prefix = text.endsWith(' ') ? text : `${text} `;\n  const reader = source.getReader();\n  return new ReadableStream<T | string>({\n    start(controller) {\n      controller.enqueue(prefix);\n    },\n    async pull(controller) {\n      try {\n        const { done, value } = await reader.read();\n        if (done) controller.close();\n        else controller.enqueue(value);\n      } catch (error) {\n        controller.error(error);\n      }\n    },\n    cancel(reason) {\n      return reader.cancel(reason);\n    },\n  });\n}\n\nexport type MastraStreamOptions = Partial<AgentExecutionOptionsBase<unknown>>;\n\nexport interface VoiceToolCall {\n  toolCallId: string;\n  toolName: string;\n  args?: unknown;\n}\n\n/**\n * Token usage for one turn, captured from the model's `finish` chunk. Field names mirror LiveKit's\n * `CompletionUsage` so the plugin can forward it into `metrics_collected` without remapping.\n */\nexport interface VoiceTurnUsage {\n  /** Tokens in the prompt (LiveKit `promptTokens`). */\n  promptTokens: number;\n  /** Tokens in the completion (LiveKit `completionTokens`). */\n  completionTokens: number;\n  /** Cached prompt tokens (LiveKit `promptCachedTokens`). */\n  promptCachedTokens: number;\n  /** Total tokens for the turn. */\n  totalTokens: number;\n}\n\n/**\n * Maps a Mastra `finish` chunk's usage (`payload.output.usage`, AI-SDK `LanguageModelUsage`) to the\n * LiveKit-shaped {@link VoiceTurnUsage}, or `undefined` when the chunk carries no token counts.\n * Handles both the flat V2 usage shape (`inputTokens`/`outputTokens`) and the nested V3 shape\n * (`inputTokens.total`/`outputTokens.total`).\n */\nexport function mapTurnUsage(usage: unknown): VoiceTurnUsage | undefined {\n  if (!usage || typeof usage !== 'object') return undefined;\n  const u = usage as Record<string, unknown>;\n  // Prefer the flat V2 shape (`inputTokens: number`); fall back to the nested V3 shape\n  // (`inputTokens: { total, cacheRead }`).\n  const totalOf = (v: unknown): number | undefined => {\n    if (typeof v === 'number') return v;\n    if (v && typeof v === 'object' && typeof (v as { total?: unknown }).total === 'number') {\n      return (v as { total: number }).total;\n    }\n    return undefined;\n  };\n  const cacheReadOf = (v: unknown): number | undefined =>\n    v && typeof v === 'object' && typeof (v as { cacheRead?: unknown }).cacheRead === 'number'\n      ? (v as { cacheRead: number }).cacheRead\n      : undefined;\n\n  const promptTokens = totalOf(u.inputTokens) ?? 0;\n  const completionTokens = totalOf(u.outputTokens) ?? 0;\n  const promptCachedTokens =\n    (typeof u.cachedInputTokens === 'number' ? u.cachedInputTokens : cacheReadOf(u.inputTokens)) ?? 0;\n  const totalTokens = typeof u.totalTokens === 'number' ? u.totalTokens : promptTokens + completionTokens;\n  // Nothing was reported at all → treat as no usage rather than emitting an all-zero chunk.\n  if (promptTokens === 0 && completionTokens === 0 && totalTokens === 0 && promptCachedTokens === 0) {\n    return undefined;\n  }\n  return { promptTokens, completionTokens, promptCachedTokens, totalTokens };\n}\n\nexport interface MastraVoiceAgentMemory {\n  thread: string;\n  resource?: string;\n  /**\n   * Per-call Mastra memory config forwarded to the agent as `memory.options` (see core's\n   * `AgentMemoryOption`). The notable use is `{ readOnly: true }`: the agent still reads thread\n   * history but persists nothing, which keeps LiveKit's preemptive (speculative) turns from writing\n   * partial user and assistant messages to the thread. With `readOnly` the caller owns persistence\n   * of committed turns (e.g. from `onTurnComplete` via `memory.saveMessages`).\n   */\n  options?: AgentMemoryOption['options'];\n}\n\n/**\n * Per-turn context handed to a {@link VoiceReplyGenerator}. LiveKit calls `llmNode` once per\n * detected user turn; the bridge builds this context and asks the generator for the reply.\n */\nexport interface VoiceTurnContext {\n  /** Stable user-turn identifier, supplied by the worker or plugin. */\n  turnId?: string;\n  /** Unique generation attempt identifier, including speculative replies and retries. */\n  attemptId?: string;\n  /** @internal Bridge-side timing collector. */\n  metrics?: ReturnType<typeof createGenerationMetrics>;\n  /**\n   * The messages to generate a reply from. With Mastra Memory on, only the messages new since\n   * the agent last spoke (history comes from the thread); with memory off, the full session.\n   *\n   * For a workflow / custom generator: pass these straight to a memory-backed `agent.stream(...,\n   * { memory })` inside a step so the agent backfills history from the thread (no duplication). A\n   * stateless workflow that wants the entire transcript every turn should read `chatCtx` instead\n   * (e.g. `chatContextToMessages(chatCtx)`), since there is no thread to backfill from.\n   */\n  messages: VoiceTurnMessage[];\n  /** The raw LiveKit chat context, for generators that want the full transcript or message parts. */\n  chatCtx: llm.ChatContext;\n  /** Resolved memory mapping for the call, or `false` when memory is disabled. */\n  memory: MastraVoiceAgentMemory | false;\n  /** Request context forwarded to generation. */\n  requestContext?: RequestContext;\n  /** Voice-call span context, so each turn's generation nests under the call trace. */\n  tracingContext?: TracingContext;\n  /**\n   * Internal, per-turn side channel for token usage. A generator invokes this once, when the\n   * `finish` chunk carries usage, so the caller (e.g. `MastraLLMStream`) can attribute usage to\n   * exactly this turn — kept on the context (not on generator options) so overlapping turns from\n   * preemptive generation can't misattribute usage. Fire-and-forget; the generator does not await it.\n   */\n  onUsage?: (usage: VoiceTurnUsage) => void;\n}\n\n/**\n * What a turn produced, handed to {@link VoiceTurnCompleteHook} after the reply finishes.\n */\nexport interface VoiceTurnResult {\n  /** The assistant reply text streamed this turn, accumulated from the model's text deltas. */\n  text: string;\n  /** Tool calls the agent made during the turn, in order. */\n  toolCalls: VoiceToolCall[];\n  /** True when barge-in cut the turn short before it finished streaming. */\n  interrupted: boolean;\n  /** Token usage for the turn when the model reported it in its `finish` chunk. */\n  usage?: VoiceTurnUsage;\n}\n\n/** {@link VoiceTurnContext} plus the reply it produced. Passed to {@link VoiceTurnCompleteHook}. */\nexport interface VoiceTurnCompleteContext extends VoiceTurnContext {\n  /** The reply the agent produced this turn. */\n  result: VoiceTurnResult;\n}\n\n/**\n * Called once per turn AFTER the reply has finished streaming to text-to-speech — off the audio\n * path. It runs fire-and-forget: the turn does not await it, so post-turn work (memory\n * maintenance, CRM writes, analytics) never delays what the caller hears or the next turn. A\n * thrown error or rejected promise is logged, not propagated. Because the resolved `memory`\n * mapping (`thread`/`resource`) is on the context, this is the place for a truly non-blocking\n * `memory.updateWorkingMemory(...)`. See {@link MastraVoiceAgentOptions.onTurnComplete}.\n */\nexport type VoiceTurnCompleteHook = (ctx: VoiceTurnCompleteContext) => void | Promise<void>;\n\n/**\n * Produces a stream of text deltas for one conversational turn, or `null` to stay silent.\n * Cancelling the returned stream (LiveKit does this on barge-in) must abort the underlying\n * generation. Built-in implementations: {@link createAgentReplyGenerator} (a Mastra agent) and\n * `createWorkflowReplyGenerator` (a Mastra workflow).\n */\nexport type VoiceReplyGenerator = (\n  ctx: VoiceTurnContext,\n) => ReadableStream<VoiceReplyChunk> | null | Promise<ReadableStream<VoiceReplyChunk> | null>;\n\nexport interface AgentReplyGeneratorOptions {\n  /** The Mastra agent that generates replies. Tools and memory run inside this agent. */\n  agent: MastraAgent;\n  /** Extra options merged into every `agent.stream()` call (e.g. `tracingContext`). */\n  streamOptions?: MastraStreamOptions;\n  /** Speak a short phrase while a tool call runs. See {@link MastraVoiceAgentOptions.toolFeedback}. */\n  toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void;\n  /** Notified as each tool-call chunk arrives, mid-stream. See {@link MastraVoiceAgentOptions.onToolCall}. */\n  onToolCall?: (toolCall: VoiceToolCall) => void;\n  /** Fired off the audio path after the reply streams. See {@link MastraVoiceAgentOptions.onTurnComplete}. */\n  onTurnComplete?: VoiceTurnCompleteHook;\n}\n\n/**\n * A {@link VoiceReplyGenerator} backed by a Mastra agent: runs the agent's full loop (model,\n * tools, memory) and streams its text deltas. On barge-in the returned stream is cancelled,\n * which aborts the in-flight `agent.stream()`.\n */\nexport function createAgentReplyGenerator(options: AgentReplyGeneratorOptions): VoiceReplyGenerator {\n  const { agent, streamOptions, toolFeedback, onToolCall, onTurnComplete } = options;\n  return ctx => {\n    if (ctx.messages.length === 0) return null;\n\n    const abortController = new AbortController();\n    const mergedOptions: MastraStreamOptions = {\n      ...streamOptions,\n      abortSignal: abortController.signal,\n    };\n    if (ctx.memory) mergedOptions.memory = ctx.memory;\n    if (ctx.requestContext) mergedOptions.requestContext = ctx.requestContext;\n\n    let cancelled = false;\n    // Accumulated as the turn streams so the post-turn hook can see what was actually produced.\n    let replyText = '';\n    const toolCalls: VoiceToolCall[] = [];\n    let usage: VoiceTurnUsage | undefined;\n\n    // Fire-and-forget after generation; playback can still be pending, and not awaited, so it never delays the next turn. Errors are logged, not thrown.\n    const emitTurnComplete = (interrupted: boolean) => {\n      if (!onTurnComplete) return;\n      const completeCtx: VoiceTurnCompleteContext = {\n        ...ctx,\n        result: { text: replyText, toolCalls, interrupted, usage },\n      };\n      Promise.resolve()\n        .then(() => onTurnComplete(completeCtx))\n        .catch(error => {\n          console.warn('@mastra/livekit: onTurnComplete hook threw', error);\n        });\n    };\n\n    return new ReadableStream<VoiceReplyChunk>({\n      start: async controller => {\n        try {\n          const result = await agent.stream(ctx.messages, mergedOptions);\n          for await (const chunk of result.fullStream) {\n            if (cancelled) break;\n            if (chunk.type === 'text-delta') {\n              if (chunk.payload.text) {\n                replyText += chunk.payload.text;\n                controller.enqueue(chunk.payload.text);\n              }\n            } else if (chunk.type === 'text-end' || chunk.type === 'step-finish') {\n              controller.enqueue(VOICE_TEXT_FLUSH);\n            } else if (chunk.type === 'tool-result') {\n              ctx.metrics?.toolEnd(chunk.payload.toolCallId);\n            } else if (chunk.type === 'tool-call') {\n              const toolCall: VoiceToolCall = {\n                toolCallId: chunk.payload.toolCallId,\n                toolName: chunk.payload.toolName,\n                args: chunk.payload.args,\n              };\n              toolCalls.push(toolCall);\n              ctx.metrics?.toolStart({ toolCallId: toolCall.toolCallId, toolName: toolCall.toolName });\n              controller.enqueue(VOICE_TEXT_FLUSH);\n              // Observer hooks are customer code: a throw must not tear down an otherwise healthy\n              // reply stream (same isolation as onTurnComplete).\n              try {\n                onToolCall?.(toolCall);\n              } catch (error) {\n                console.warn('@mastra/livekit: onToolCall hook threw', error);\n              }\n              if (toolFeedback) {\n                let filler: string | undefined | void;\n                try {\n                  filler = toolFeedback(toolCall);\n                } catch (error) {\n                  console.warn('@mastra/livekit: toolFeedback hook threw', error);\n                }\n                if (filler) {\n                  controller.enqueue(filler.endsWith(' ') ? filler : `${filler} `);\n                  controller.enqueue(VOICE_TEXT_FLUSH);\n                }\n              }\n            } else if (chunk.type === 'finish') {\n              // Usage is dropped from the spoken stream but surfaced via the per-turn side channel\n              // and on the turn result, so the plugin and onTurnComplete consumers can read it.\n              const output = (chunk.payload as { output?: { usage?: unknown } }).output;\n              const turnUsage = mapTurnUsage(output?.usage);\n              if (turnUsage) {\n                usage = turnUsage;\n                try {\n                  ctx.onUsage?.(turnUsage);\n                } catch (error) {\n                  console.warn('@mastra/livekit: onUsage hook threw', error);\n                }\n              }\n            } else if (chunk.type === 'error') {\n              const error = chunk.payload.error;\n              throw error instanceof Error ? error : new Error(String(error));\n            }\n          }\n          if (!cancelled) controller.close();\n          // Success, or a clean barge-in break out of the loop: the turn is done either way.\n          emitTurnComplete(cancelled);\n        } catch (error) {\n          // Barge-in cancels the stream and aborts generation; that's not a failure — the turn\n          // still completed (interrupted), so the hook still fires for memory reconciliation.\n          if (cancelled || abortController.signal.aborted) {\n            emitTurnComplete(true);\n            return;\n          }\n          controller.error(error);\n        }\n      },\n      cancel: () => {\n        cancelled = true;\n        abortController.abort();\n      },\n    });\n  };\n}\n\nexport interface MastraVoiceAgentOptions {\n  /** Receives per-attempt generation metrics. Playback metrics come from observeVoiceSession. */\n  onTurnMetrics?: VoiceTurnMetricsHook;\n  /**\n   * The Mastra agent that generates replies. Tools and memory run inside this agent. Provide\n   * either this or {@link MastraVoiceAgentOptions.generate}.\n   */\n  agent?: MastraAgent;\n  /**\n   * A lower-level reply generator (e.g. from `createWorkflowReplyGenerator`). Use instead of\n   * `agent` to drive replies with a workflow or any custom generator.\n   */\n  generate?: VoiceReplyGenerator;\n  /**\n   * Conversation persistence. When set, only messages new since the agent last spoke are\n   * sent each turn and Mastra Memory supplies history. When `false`, the full LiveKit\n   * in-session context is sent on every turn instead.\n   */\n  memory?: MastraVoiceAgentMemory | false;\n  /** Request context entries forwarded to generation. */\n  requestContext?: RequestContext | Record<string, unknown>;\n  /**\n   * Called when the Mastra agent starts a tool call mid-reply. Return a short phrase (e.g. \"Let\n   * me look that up.\") to speak it while the tool runs; it also appears in the transcript. Return\n   * nothing to stay silent. Applies to the agent generator built here; the workflow generator\n   * takes its own equivalent via `createWorkflowReplyGenerator`.\n   */\n  toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void;\n  /**\n   * Called as each tool call starts mid-reply (before the tool result is known), the building block\n   * for tool-driven side effects — analytics, agent-initiated hang-up — without waiting for the turn\n   * to finish. Runs synchronously on the stream; keep it cheap and non-throwing. Applies to the agent\n   * generator built here; the workflow generator surfaces tool calls via `onTurnComplete` instead.\n   */\n  onToolCall?: (toolCall: VoiceToolCall) => void;\n  /**\n   * Called once per turn after the reply has finished streaming to text-to-speech. Runs off the\n   * audio path and fire-and-forget — the turn does not await it — so post-turn memory\n   * maintenance, CRM writes, or analytics never delay the caller or the next turn. The context\n   * carries the produced reply ({@link VoiceTurnResult}) and the resolved `memory` mapping, so\n   * this is where a truly non-blocking `memory.updateWorkingMemory(...)` belongs. A thrown error\n   * or rejected promise is logged, not propagated. Applies to the agent generator built here; the\n   * workflow generator takes its own via `createWorkflowReplyGenerator`.\n   */\n  onTurnComplete?: VoiceTurnCompleteHook;\n  /**\n   * Periodic AI re-disclosure. When set, once `everyMs` has elapsed since the last disclosure the\n   * NEXT turn's reply is prefixed with `text` (spoken at the turn boundary, never mid-turn), so long\n   * calls keep re-disclosing the AI status. Applies to the agent and workflow/custom generators. The\n   * worker derives this from `configuration.greeting.repeatEvery` / `repeatText`; `text` defaults to\n   * {@link DEFAULT_DISCLOSURE_REMINDER}.\n   */\n  greetingReminder?: { everyMs: number; text?: string };\n  /** Extra options merged into every `agent.stream()` call (agent generator only). */\n  streamOptions?: MastraStreamOptions;\n  /** LiveKit agent instructions. Unused for reply generation (the Mastra agent/workflow applies its own). */\n  instructions?: string;\n  id?: voice.AgentOptions<unknown>['id'];\n  stt?: voice.AgentOptions<unknown>['stt'];\n  vad?: voice.AgentOptions<unknown>['vad'];\n  tts?: voice.AgentOptions<unknown>['tts'];\n  turnHandling?: voice.AgentOptions<unknown>['turnHandling'];\n}\n\nfunction toRequestContext(value: RequestContext | Record<string, unknown> | undefined): RequestContext | undefined {\n  if (!value) return undefined;\n  if (value instanceof RequestContext) return value;\n  return new RequestContext<unknown>(Object.entries(value));\n}\n\n/**\n * The session only runs its cascaded reply pipeline when an `llm` instance is present —\n * `llmNode` replaces the inference step, but the gate checks `llm instanceof LLM`. This\n * placeholder satisfies the gate; the Mastra agent/workflow does the actual generation.\n */\nclass MastraPlaceholderLLM extends llm.LLM {\n  label(): string {\n    return 'mastra.MastraVoiceAgent';\n  }\n\n  override get model(): string {\n    return 'mastra-agent';\n  }\n\n  override get provider(): string {\n    return 'mastra';\n  }\n\n  chat(): llm.LLMStream {\n    throw new Error(\n      '@mastra/livekit: reply generation runs through the Mastra agent via llmNode; the placeholder LLM cannot be used for inference.',\n    );\n  }\n}\n\n/**\n * A LiveKit `voice.Agent` whose replies come from a Mastra agent or workflow.\n *\n * LiveKit keeps ownership of the audio loop (VAD, STT, turn detection, TTS, barge-in) and calls\n * `llmNode` once per detected user turn; the node delegates to a {@link VoiceReplyGenerator}\n * which streams text deltas back. On barge-in LiveKit cancels the returned stream, which aborts\n * the in-flight generation.\n */\nexport class MastraVoiceAgent extends voice.Agent {\n  readonly mastraAgent?: MastraAgent;\n  readonly memory: MastraVoiceAgentMemory | false;\n  readonly requestContext?: RequestContext;\n  readonly streamOptions?: MastraStreamOptions;\n  private readonly replyGenerator: VoiceReplyGenerator;\n  private readonly onTurnMetrics?: VoiceTurnMetricsHook;\n  private readonly reminder?: DisclosureReminder;\n\n  constructor(options: MastraVoiceAgentOptions) {\n    if (options.agent && options.generate) {\n      throw new Error(\n        '@mastra/livekit: MastraVoiceAgent requires `agent` or `generate`, not both — they are mutually exclusive reply sources.',\n      );\n    }\n    super({\n      id: options.id,\n      instructions: options.instructions ?? DEFAULT_INSTRUCTIONS,\n      stt: options.stt,\n      vad: options.vad,\n      llm: new MastraPlaceholderLLM(),\n      tts: options.tts,\n      turnHandling: options.turnHandling,\n    });\n    this.onTurnMetrics = options.onTurnMetrics;\n    this.memory = options.memory ?? false;\n    this.requestContext = toRequestContext(options.requestContext);\n    this.streamOptions = options.streamOptions;\n    if (options.greetingReminder) {\n      this.reminder = new DisclosureReminder(\n        options.greetingReminder.everyMs,\n        options.greetingReminder.text?.trim() || DEFAULT_DISCLOSURE_REMINDER,\n      );\n    }\n\n    if (options.generate) {\n      this.replyGenerator = options.generate;\n    } else if (options.agent) {\n      this.mastraAgent = options.agent;\n      this.replyGenerator = createAgentReplyGenerator({\n        agent: options.agent,\n        streamOptions: options.streamOptions,\n        toolFeedback: options.toolFeedback,\n        onToolCall: options.onToolCall,\n        onTurnComplete: options.onTurnComplete,\n      });\n    } else {\n      throw new Error('@mastra/livekit: MastraVoiceAgent requires `agent` or `generate`.');\n    }\n  }\n\n  override async llmNode(\n    chatCtx: llm.ChatContext,\n    _toolCtx: llm.ToolContext,\n    _modelSettings: voice.ModelSettings,\n  ): Promise<ReadableStream<llm.ChatChunk | string | FlushSentinel> | null> {\n    const messages: VoiceTurnMessage[] =\n      this.memory === false ? chatContextToMessages(chatCtx) : extractNewTurnMessages(chatCtx);\n    if (messages.length === 0) return null;\n\n    const ctx = {\n      messages,\n      chatCtx,\n      memory: this.memory,\n      requestContext: this.requestContext,\n      tracingContext: this.streamOptions?.tracingContext,\n    };\n    const { reply, identity } = await instrumentVoiceReply(this.replyGenerator, ctx, this.onTurnMetrics);\n    if (!reply) return null;\n\n    // Periodic AI re-disclosure: when the interval has elapsed, prefix this turn's spoken reply with\n    // the reminder. Done at the turn boundary (never mid-turn), riding the same stream so barge-in\n    // cancellation still propagates to the underlying generation. The clock only resets once the\n    // reminder is actually threaded into the outgoing reply below, not just because it was due.\n    // KNOWN LIMIT: \"threaded into the reply\" is stream-build time, not playout. Under LiveKit's\n    // preemptive generation a discarded speculative reply still resets the clock, so the next real\n    // turn can miss its reminder — hence the documented repeatEvery/preemptiveGeneration\n    // incompatibility. A playout-accurate reset needs a confirmed-turn signal llmNode doesn't have.\n    const reminder = this.reminder?.due();\n    if (reminder) this.reminder?.markDelivered();\n    const source = reminder ? prependText(reply, reminder) : reply;\n    return mapVoiceStream(source, chunk => {\n      if (typeof chunk === 'string') {\n        return {\n          id: identity.attemptId,\n          delta: { role: 'assistant' as const, content: chunk, extra: { [VOICE_TURN_METADATA]: identity } },\n        };\n      } else {\n        return FlushSentinel;\n      }\n    });\n  }\n}\n\nexport function createMastraVoiceAgent(options: MastraVoiceAgentOptions): MastraVoiceAgent {\n  return new MastraVoiceAgent(options);\n}\n\n/** @internal Wrap every reply source, including custom generators, with one terminal metric. */\nexport async function instrumentVoiceReply(\n  generator: VoiceReplyGenerator,\n  ctx: VoiceTurnContext,\n  onTurnMetrics?: VoiceTurnMetricsHook,\n): Promise<{ reply: ReadableStream<VoiceReplyChunk> | null; identity: { turnId: string; attemptId: string } }> {\n  const identity = {\n    turnId: ctx.turnId ?? ctx.messages.findLast(message => message.role === 'user')?.id ?? crypto.randomUUID(),\n    attemptId: crypto.randomUUID(),\n  };\n  const metrics = createGenerationMetrics(identity, onTurnMetrics);\n  let reply: ReadableStream<VoiceReplyChunk> | null;\n  try {\n    reply = await generator({ ...ctx, ...identity, metrics });\n  } catch (error) {\n    metrics.end('failed');\n    throw error;\n  }\n  if (!reply) {\n    metrics.end('completed');\n    return { reply: null, identity };\n  }\n  const reader = reply.getReader();\n  let cancelled = false;\n  return {\n    identity,\n    reply: new ReadableStream<VoiceReplyChunk>({\n      async pull(controller) {\n        try {\n          const { done, value } = await reader.read();\n          if (cancelled) return;\n          if (done) {\n            metrics.end('completed');\n            controller.close();\n          } else {\n            if (typeof value === 'string' && value) metrics.text();\n            controller.enqueue(value);\n          }\n        } catch (error) {\n          if (cancelled) return;\n          metrics.end('failed');\n          controller.error(error);\n        }\n      },\n      cancel(reason) {\n        cancelled = true;\n        metrics.end('interrupted');\n        return reader.cancel(reason);\n      },\n    }),\n  };\n}\n","import { FlushSentinel, voice } from '@livekit/agents';\nimport type { llm } from '@livekit/agents';\nimport { notifyVoiceObserver, VOICE_FLUSH_METADATA, VOICE_TURN_METADATA } from './turn-metrics';\nimport type { VoiceOutcome, VoiceSpeechCompleteHook, VoiceSpeechResult, VoiceTurnMetricsHook } from './turn-metrics';\nimport { mapVoiceStream } from './voice-stream';\n\n/**\n * Use from a custom Agent.llmNode when using MastraLLM. LiveKit's LLMStream only accepts\n * ChatChunks; this node translates our boundary metadata into LiveKit's supported marker.\n */\nexport async function mastraLLMNode(\n  agent: voice.Agent,\n  chatCtx: llm.ChatContext,\n  toolCtx: llm.ToolContext,\n  modelSettings: voice.ModelSettings,\n): Promise<ReadableStream<llm.ChatChunk | string | FlushSentinel> | null> {\n  const source = await voice.Agent.default.llmNode(agent, chatCtx, toolCtx, modelSettings);\n  if (!source) return null;\n  return mapVoiceStream(source, chunk => {\n    if (typeof chunk === 'object' && chunk.delta?.extra?.[VOICE_FLUSH_METADATA] === true) {\n      return FlushSentinel;\n    } else {\n      return chunk;\n    }\n  });\n}\n\nexport interface ObserveVoiceSessionOptions {\n  onTurnMetrics?: VoiceTurnMetricsHook;\n  onSpeechComplete?: VoiceSpeechCompleteHook;\n}\n\nfunction speechResult(handle: voice.SpeechHandle, outcome: VoiceOutcome): VoiceSpeechResult {\n  const messages = handle.chatItems.filter(\n    (item): item is llm.ChatMessage => item.type === 'message' && item.role === 'assistant',\n  );\n  const identities = messages.map(\n    message => message.extra[VOICE_TURN_METADATA] as { turnId?: unknown; attemptId?: unknown } | undefined,\n  );\n  const identity = identities[0];\n  const sameIdentity = identities.every(\n    value => value?.turnId === identity?.turnId && value?.attemptId === identity?.attemptId,\n  );\n  const starts = messages\n    .map(message => message.metrics.startedSpeakingAt)\n    .filter((n): n is number => typeof n === 'number' && Number.isFinite(n));\n  const stops = messages\n    .map(message => message.metrics.stoppedSpeakingAt)\n    .filter((n): n is number => typeof n === 'number' && Number.isFinite(n));\n  const firstAudio = messages\n    .map(message => message.metrics.e2eLatency)\n    .find(n => typeof n === 'number' && Number.isFinite(n));\n  const speechStartedAt = starts.length ? Math.min(...starts) * 1000 : undefined;\n  const speechEndedAt = stops.length ? Math.max(...stops) * 1000 : undefined;\n  const firstAudioMs = firstAudio === undefined ? undefined : firstAudio * 1000;\n  const playedText = messages.map(message => message.textContent).join('');\n  return {\n    version: 1,\n    phase: 'speech',\n    turnId: sameIdentity && typeof identity?.turnId === 'string' ? identity.turnId : handle.id,\n    attemptId: sameIdentity && typeof identity?.attemptId === 'string' ? identity.attemptId : undefined,\n    speechId: handle.id,\n    outcome,\n    measurement: 'server-playout',\n    speechStartedAt,\n    speechEndedAt,\n    firstAudioMs,\n    completionMs:\n      outcome === 'completed' &&\n      firstAudioMs !== undefined &&\n      speechStartedAt !== undefined &&\n      speechEndedAt !== undefined\n        ? firstAudioMs + speechEndedAt - speechStartedAt\n        : undefined,\n    playedText: messages.length ? playedText : undefined,\n    transcriptSource: messages.length ? 'livekit' : 'unavailable',\n  };\n}\n\n/**\n * Attach before session.start(). Observes generation-independent playback completion;\n * never writes memory. Return value detaches listeners and finalizes pending speech as cancelled.\n * LiveKit's transcript can be estimated. Server output cannot confirm remote human hearing.\n */\nexport function observeVoiceSession(session: voice.AgentSession, options: ObserveVoiceSessionOptions): () => void {\n  const pending = new Set<voice.SpeechHandle>();\n  let detached = false;\n  const finish = (handle: voice.SpeechHandle, outcome: VoiceOutcome) => {\n    if (!pending.delete(handle)) return;\n    const result = speechResult(handle, outcome);\n    const { playedText: _text, transcriptSource: _source, ...metrics } = result;\n    notifyVoiceObserver(options.onTurnMetrics, metrics);\n    notifyVoiceObserver(options.onSpeechComplete, result);\n  };\n  const onSpeech = ({ speechHandle }: voice.SpeechCreatedEvent) => {\n    if (detached || pending.has(speechHandle)) return;\n    pending.add(speechHandle);\n    void speechHandle.waitForPlayout().then(\n      () => {\n        finish(\n          speechHandle,\n          speechHandle.exception() ? 'failed' : speechHandle.interrupted ? 'interrupted' : 'completed',\n        );\n      },\n      () => finish(speechHandle, 'failed'),\n    );\n  };\n  const detach = (outcome: VoiceOutcome) => {\n    if (detached) return;\n    detached = true;\n    session.off(voice.AgentSessionEventTypes.SpeechCreated, onSpeech);\n    session.off(voice.AgentSessionEventTypes.Close, onClose);\n    for (const handle of pending) finish(handle, outcome);\n  };\n  const onClose = (event: voice.CloseEvent) => detach(event.error ? 'failed' : 'cancelled');\n  session.on(voice.AgentSessionEventTypes.SpeechCreated, onSpeech);\n  session.on(voice.AgentSessionEventTypes.Close, onClose);\n  return () => detach('cancelled');\n}\n","import { ReadableStream } from 'node:stream/web';\nimport { APIConnectionError, APIError, APIStatusError, APITimeoutError } from '@livekit/agents';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { mapTurnUsage } from './bridge';\nimport type {\n  VoiceReplyGenerator,\n  VoiceToolCall,\n  VoiceTurnCompleteContext,\n  VoiceTurnCompleteHook,\n  VoiceTurnUsage,\n} from './bridge';\nimport { VOICE_TEXT_FLUSH } from './turn-metrics';\nimport type { VoiceReplyChunk } from './turn-metrics';\n\nconst DEFAULT_API_PREFIX = '/api';\n/** Connect + first-token budget when not overridden. Plugin mode passes `connOptions.timeoutMs`. */\nexport const DEFAULT_REMOTE_TIMEOUT_MS = 10_000;\n/** Standalone initial-connection retry attempts. Plugin mode forces this to 0 (base class owns retries). */\nexport const DEFAULT_REMOTE_RETRIES = 2;\n\n/** Thrown (as a plain, non-retryable error) when the server emits a chunk that needs client action. */\nconst HITL_UNSUPPORTED_MESSAGE =\n  '@mastra/livekit: the agent requested tool approval or suspended a tool call; human-in-the-loop ' +\n  'flows (approve-tool-call / resume-stream) are not supported on the voice path. Remove requireApproval ' +\n  'or suspend from the tools this agent uses on voice calls.';\n\n/**\n * Options for the remote Mastra transport. Shape mirrors the in-process `AgentReplyGeneratorOptions`\n * so `MastraLLM` can accept either source interchangeably.\n */\nexport interface RemoteMastraAgentOptions {\n  /** Base URL of the remote Mastra server, e.g. `https://my-app.mastra.cloud`. */\n  baseUrl: string;\n  /** Agent key in the Mastra config's `agents`. */\n  agentId: string;\n  /** Path prefix for the Mastra API. Defaults to `'/api'`. */\n  apiPrefix?: string;\n  /** Static headers, or a (possibly async) resolver invoked per turn — e.g. to mint a fresh token. */\n  headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);\n  /** Injectable `fetch` for tests/proxies. Defaults to `globalThis.fetch`. */\n  fetch?: typeof globalThis.fetch;\n  /**\n   * Connect + first-token timeout in ms. Plugin mode default: LiveKit's `connOptions.timeoutMs` (10s).\n   * Standalone default: {@link DEFAULT_REMOTE_TIMEOUT_MS}.\n   */\n  timeoutMs?: number;\n  /**\n   * Initial-connection retry attempts (before the first chunk only). Standalone default:\n   * {@link DEFAULT_REMOTE_RETRIES}. In plugin mode the LiveKit base class owns retries and this is\n   * forced to 0.\n   */\n  retries?: number;\n  /** Extra fields merged into each stream request body (advanced). */\n  body?: Record<string, unknown>;\n}\n\n/** {@link RemoteMastraAgentOptions} plus the per-turn observer hooks the generator threads through. */\nexport interface RemoteAgentReplyGeneratorOptions extends RemoteMastraAgentOptions {\n  /** Speak a short phrase while a tool runs. See {@link MastraVoiceAgentOptions.toolFeedback}. */\n  toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void;\n  /** Notified as each tool-call chunk arrives, mid-stream. See {@link MastraVoiceAgentOptions.onToolCall}. */\n  onToolCall?: (toolCall: VoiceToolCall) => void;\n  /** Fired off the audio path after the reply streams. See {@link MastraVoiceAgentOptions.onTurnComplete}. */\n  onTurnComplete?: VoiceTurnCompleteHook;\n}\n\ntype RawChunk = { type?: string; payload?: Record<string, unknown> };\n\nfunction trimTrailingSlash(url: string): string {\n  return url.endsWith('/') ? url.slice(0, -1) : url;\n}\n\nfunction toMessage(error: unknown): string {\n  return error instanceof Error ? error.message : String(error);\n}\n\nasync function resolveHeaders(headers: RemoteMastraAgentOptions['headers']): Promise<Record<string, string>> {\n  if (!headers) return {};\n  if (typeof headers === 'function') return (await headers()) ?? {};\n  return headers;\n}\n\nfunction serializeRequestContext(\n  requestContext: RequestContext | Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n  if (!requestContext) return undefined;\n  // Mirror client-js `parseClientRequestContext`.\n  if (requestContext instanceof RequestContext) return Object.fromEntries(requestContext.entries());\n  return requestContext;\n}\n\nasync function safeReadBody(response: Response): Promise<object | null> {\n  try {\n    const text = await response.text();\n    if (!text) return null;\n    try {\n      const parsed: unknown = JSON.parse(text);\n      return parsed && typeof parsed === 'object' ? (parsed as object) : { message: String(parsed) };\n    } catch {\n      return { message: text };\n    }\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Reads a Mastra SSE stream and yields each event's parsed JSON. Framing matches the server's\n * `processMastraStream` (buffer, split on `\\n\\n`, strip `data: `, stop on `[DONE]`); undecodable\n * `data:` lines are skipped. Aborting `signal` cancels the underlying reader.\n */\nexport async function* readMastraSSE(\n  body: globalThis.ReadableStream<Uint8Array>,\n  signal: AbortSignal,\n): AsyncGenerator<RawChunk> {\n  const reader = body.getReader();\n  const decoder = new TextDecoder();\n  let buffer = '';\n  const onAbort = () => void reader.cancel().catch(() => {});\n  if (signal.aborted) {\n    void reader.cancel().catch(() => {});\n    return;\n  }\n  signal.addEventListener('abort', onAbort, { once: true });\n  try {\n    for (;;) {\n      const { done, value } = await reader.read();\n      if (done) break;\n      buffer += decoder.decode(value, { stream: true });\n      const events = buffer.split('\\n\\n');\n      buffer = events.pop() ?? '';\n      for (const event of events) {\n        if (!event.startsWith('data:')) continue;\n        const data = event.slice(event.startsWith('data: ') ? 6 : 5).trim();\n        if (data === '[DONE]') return;\n        if (!data) continue;\n        let json: unknown;\n        try {\n          json = JSON.parse(data);\n        } catch {\n          continue; // tolerate a stray non-JSON line\n        }\n        if (json && typeof json === 'object') yield json as RawChunk;\n      }\n    }\n  } finally {\n    signal.removeEventListener('abort', onAbort);\n    try {\n      reader.releaseLock();\n    } catch {\n      // A read may still be pending when the fetch was aborted; the stream is torn down anyway.\n    }\n  }\n}\n\n/**\n * A {@link VoiceReplyGenerator} that runs the Mastra agent loop on a **remote** Mastra server over\n * HTTP/SSE. Shaped exactly like the in-process `createAgentReplyGenerator`: it consumes the same\n * chunk vocabulary, drives the same `toolFeedback` / `onToolCall` / `onTurnComplete` seams, and\n * cancelling the returned stream (LiveKit does this on barge-in) tears down the HTTP request so the\n * server aborts generation.\n *\n * Usable standalone via the worker's `generate:` hatch (a minimum-viable remote worker mode), and as\n * the transport `MastraLLM` wraps. Errors are thrown as LiveKit `APIError` subclasses so the plugin's\n * base-class retry loop and `FallbackAdapter` behave; a connect + first-token watchdog prevents\n * indefinite dead air.\n */\nexport function createRemoteAgentReplyGenerator(options: RemoteAgentReplyGeneratorOptions): VoiceReplyGenerator {\n  const {\n    baseUrl,\n    agentId,\n    apiPrefix = DEFAULT_API_PREFIX,\n    headers,\n    fetch: fetchImpl = globalThis.fetch,\n    timeoutMs = DEFAULT_REMOTE_TIMEOUT_MS,\n    retries = DEFAULT_REMOTE_RETRIES,\n    body: extraBody,\n    toolFeedback,\n    onToolCall,\n    onTurnComplete,\n  } = options;\n\n  if (!fetchImpl) {\n    throw new Error('@mastra/livekit: no fetch implementation available; pass `fetch` or run on Node ≥ 22.');\n  }\n  const url = `${trimTrailingSlash(baseUrl)}${apiPrefix}/agents/${agentId}/stream`;\n\n  return ctx => {\n    if (ctx.messages.length === 0) return null;\n\n    // Reassigned per retry attempt (see the loop below) so a watchdog abort on one attempt can't\n    // poison the next; `cancel()` always aborts whichever attempt is currently in flight.\n    let currentAbortController: AbortController | undefined;\n    let cancelled = false;\n    // Accumulated as the turn streams so the post-turn hook sees what was actually produced.\n    let replyText = '';\n    const toolCalls: VoiceToolCall[] = [];\n    let usage: VoiceTurnUsage | undefined;\n\n    const emitTurnComplete = (interrupted: boolean) => {\n      if (!onTurnComplete) return;\n      const completeCtx: VoiceTurnCompleteContext = {\n        ...ctx,\n        result: { text: replyText, toolCalls, interrupted, usage },\n      };\n      Promise.resolve()\n        .then(() => onTurnComplete(completeCtx))\n        .catch(error => {\n          console.warn('@mastra/livekit: onTurnComplete hook threw', error);\n        });\n    };\n\n    const requestBody: Record<string, unknown> = {\n      messages: ctx.messages,\n      // The server schema requires a resource when memory is present; default it to the thread id,\n      // matching the worker's own thread bootstrap.\n      memory: ctx.memory\n        ? {\n            thread: ctx.memory.thread,\n            resource: ctx.memory.resource ?? ctx.memory.thread,\n            ...(ctx.memory.options ? { options: ctx.memory.options } : {}),\n          }\n        : undefined,\n      requestContext: serializeRequestContext(ctx.requestContext),\n      ...extraBody,\n    };\n\n    return new ReadableStream<VoiceReplyChunk>({\n      start: async controller => {\n        // `retryable` is the LiveKit contract flag: true only before the first chunk is emitted, so a\n        // voice turn is never replayed half-heard. It also gates the standalone connect-retry.\n        let retryable = true;\n        try {\n          for (let attempt = 0; ; attempt++) {\n            // A fresh controller per attempt: reusing one across retries meant a watchdog abort on an\n            // earlier attempt left every subsequent attempt's fetch already-aborted before it started.\n            const abortController = new AbortController();\n            currentAbortController = abortController;\n            let timedOut = false;\n            let watchdog: ReturnType<typeof setTimeout> | undefined;\n            const clearWatchdog = () => {\n              if (watchdog) {\n                clearTimeout(watchdog);\n                watchdog = undefined;\n              }\n            };\n            try {\n              watchdog = setTimeout(() => {\n                timedOut = true;\n                abortController.abort();\n              }, timeoutMs);\n              (watchdog as { unref?: () => void }).unref?.();\n\n              const resolvedHeaders = await resolveHeaders(headers);\n              if (cancelled) {\n                clearWatchdog();\n                break;\n              }\n              const response = await fetchImpl(url, {\n                method: 'POST',\n                headers: { 'content-type': 'application/json', accept: 'text/event-stream', ...resolvedHeaders },\n                body: JSON.stringify(requestBody),\n                signal: abortController.signal,\n              });\n              if (!response.ok) {\n                const errorBody = await safeReadBody(response);\n                throw new APIStatusError({\n                  message: `@mastra/livekit: Mastra agent stream request failed with status ${response.status}`,\n                  options: { statusCode: response.status, body: errorBody, retryable },\n                });\n              }\n              if (!response.body) {\n                throw new APIConnectionError({\n                  message: '@mastra/livekit: Mastra agent stream returned an empty response body',\n                  options: { retryable },\n                });\n              }\n\n              for await (const chunk of readMastraSSE(\n                response.body as unknown as globalThis.ReadableStream<Uint8Array>,\n                abortController.signal,\n              )) {\n                if (cancelled) break;\n                // First chunk: the server has committed to this generation — forbid any further\n                // retry so the turn can't be replayed mid-stream. The watchdog is NOT cleared here:\n                // lifecycle metadata (step-start, text-start, ...) isn't proof the model is\n                // producing anything, and disarming on it would turn a post-metadata stall into\n                // indefinite dead air. It disarms on the first sign of model output below.\n                retryable = false;\n                const payload = chunk.payload ?? {};\n                switch (chunk.type) {\n                  case 'text-delta': {\n                    const text = payload.text;\n                    if (typeof text === 'string' && text) {\n                      clearWatchdog();\n                      replyText += text;\n                      controller.enqueue(text);\n                    }\n                    break;\n                  }\n                  case 'text-end':\n                  case 'step-finish':\n                    controller.enqueue(VOICE_TEXT_FLUSH);\n                    break;\n                  case 'tool-result':\n                    if (typeof payload.toolCallId === 'string') ctx.metrics?.toolEnd(payload.toolCallId);\n                    break;\n                  case 'tool-call': {\n                    // A tool call is first-token progress too — the model committed to a tool run,\n                    // which may legitimately outlast the connect budget before any text streams.\n                    clearWatchdog();\n                    const toolCall: VoiceToolCall = {\n                      toolCallId: String(payload.toolCallId ?? ''),\n                      toolName: String(payload.toolName ?? ''),\n                      args: payload.args,\n                    };\n                    toolCalls.push(toolCall);\n                    ctx.metrics?.toolStart({ toolCallId: toolCall.toolCallId, toolName: toolCall.toolName });\n                    controller.enqueue(VOICE_TEXT_FLUSH);\n                    // Observer hooks are customer code: a throw must not tear down an otherwise\n                    // healthy reply stream (same isolation as onTurnComplete).\n                    try {\n                      onToolCall?.(toolCall);\n                    } catch (error) {\n                      console.warn('@mastra/livekit: onToolCall hook threw', error);\n                    }\n                    if (toolFeedback) {\n                      let filler: string | undefined | void;\n                      try {\n                        filler = toolFeedback(toolCall);\n                      } catch (error) {\n                        console.warn('@mastra/livekit: toolFeedback hook threw', error);\n                      }\n                      if (filler) {\n                        controller.enqueue(filler.endsWith(' ') ? filler : `${filler} `);\n                        controller.enqueue(VOICE_TEXT_FLUSH);\n                      }\n                    }\n                    break;\n                  }\n                  case 'finish': {\n                    clearWatchdog();\n                    const output = payload.output as { usage?: unknown } | undefined;\n                    const turnUsage = mapTurnUsage(output?.usage);\n                    if (turnUsage) {\n                      usage = turnUsage;\n                      try {\n                        ctx.onUsage?.(turnUsage);\n                      } catch (error) {\n                        console.warn('@mastra/livekit: onUsage hook threw', error);\n                      }\n                    }\n                    break;\n                  }\n                  case 'tool-call-approval':\n                  case 'tool-call-suspended':\n                    throw new Error(HITL_UNSUPPORTED_MESSAGE);\n                  case 'error': {\n                    const error = payload.error;\n                    throw error instanceof Error ? error : new Error(String(error));\n                  }\n                  default:\n                    break; // ignore everything else (text-start, step-start, tool-result, ...)\n                }\n              }\n              clearWatchdog();\n              break; // success, or a clean barge-in break out of the SSE loop\n            } catch (error) {\n              clearWatchdog();\n              if (cancelled) break; // barge-in: not a failure, emit interrupted below\n\n              // Classify into the LiveKit error vocabulary. Application errors thrown after streaming\n              // began (an `error` chunk, a HITL chunk) are not connection failures — propagate as-is.\n              let typed: APIError;\n              if (timedOut) {\n                typed = new APITimeoutError({ options: { retryable } });\n              } else if (error instanceof APIError) {\n                typed = error;\n              } else if (retryable) {\n                typed = new APIConnectionError({ message: toMessage(error), options: { retryable } });\n              } else {\n                throw error;\n              }\n              if (typed.retryable && attempt < retries) continue;\n              throw typed;\n            }\n          }\n          if (!cancelled) controller.close();\n          // Success or clean barge-in: the turn is done either way.\n          emitTurnComplete(cancelled);\n        } catch (error) {\n          // Barge-in never reaches here (handled above); a real failure errors the stream and does\n          // not fire onTurnComplete — same contract as the in-process generator.\n          if (cancelled) {\n            emitTurnComplete(true);\n            return;\n          }\n          controller.error(error);\n        }\n      },\n      cancel: () => {\n        cancelled = true;\n        currentAbortController?.abort();\n      },\n    });\n  };\n}\n"],"mappings":"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"}