{"version":3,"file":"worker-entry.cjs","names":["getOrCreateSpan","SpanType","metrics","voice","voice","InferenceRunner","defineAgent","parseSessionMetadata","RequestContext","createWorkflowReplyGenerator","createMastraVoiceAgent","observeVoiceSession","fileURLToPath","ServerOptions"],"sources":["../src/observability.ts","../src/voice-thread.ts","../src/worker-setup.ts","../src/worker.ts","../src/run.ts"],"sourcesContent":["import { metrics, voice } from '@livekit/agents';\nimport type { Mastra } from '@mastra/core/mastra';\nimport { getOrCreateSpan, SpanType } from '@mastra/core/observability';\nimport type { Span, TracingContext } from '@mastra/core/observability';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport type { VoiceTurnMetrics } from './turn-metrics';\n\nexport interface VoiceCallObservabilityOptions {\n  /** The Mastra instance whose observability config receives the spans. */\n  mastra: Mastra;\n  /** Resolved Mastra agent id/key answering the call (for span attribution). */\n  agentId: string;\n  /** LiveKit room name for this session. */\n  roomName: string;\n  /** Dispatch metadata for the session. */\n  metadata: LiveKitSessionMetadata;\n  /** Request context forwarded to span sampling. */\n  requestContext?: RequestContext;\n}\n\nexport interface VoiceCallObservability {\n  /** The root `voice call` span. Each turn's agent run and every pipeline metric nests under it. */\n  readonly span: Span<SpanType.GENERIC>;\n  /** Thread into the bridge's `streamOptions` so each turn's agent run nests under the call. */\n  readonly tracingContext: TracingContext;\n  /** Subscribe to the session's `metrics_collected` events. Call once, before `session.start()`. */\n  attach(session: voice.AgentSession): void;\n  /** Record a generation or playback measurement with its correlation IDs. */\n  recordTurn(metric: VoiceTurnMetrics): void;\n  /** Close the call span with the usage roll-up. Idempotent; safe to call from a shutdown hook. */\n  finalize(options?: { error?: unknown }): void;\n}\n\nfunction modelMeta(metadata?: metrics.MetricsMetadata): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  if (metadata?.modelProvider) out.modelProvider = metadata.modelProvider;\n  if (metadata?.modelName) out.modelName = metadata.modelName;\n  return out;\n}\n\nconst ms = (n: number) => `${Math.round(n)}ms`;\nconst secs = (n: number) => `${(n / 1000).toFixed(1)}s`;\n\n/**\n * Maps a LiveKit pipeline metric to an event-span name and the latency/usage fields worth\n * recording. The name carries the headline number so it reads in the trace timeline without\n * opening the span. Metrics this release does not surface (interruption, EOT inference, avatar)\n * return `undefined` — they still feed the session usage roll-up via the ModelUsageCollector.\n */\nfunction describeMetric(metric: metrics.AgentMetrics): { name: string; data: Record<string, unknown> } | undefined {\n  switch (metric.type) {\n    case 'eou_metrics':\n      // End-of-utterance: how long turn detection took to decide the caller finished.\n      return {\n        name: `eou ${ms(metric.endOfUtteranceDelayMs)}`,\n        data: {\n          endOfUtteranceDelayMs: metric.endOfUtteranceDelayMs,\n          transcriptionDelayMs: metric.transcriptionDelayMs,\n          onUserTurnCompletedDelayMs: metric.onUserTurnCompletedDelayMs,\n        },\n      };\n    case 'stt_metrics':\n      return {\n        name: `stt ${secs(metric.audioDurationMs)}`,\n        data: {\n          audioDurationMs: metric.audioDurationMs,\n          durationMs: metric.durationMs,\n          streamed: metric.streamed,\n          ...modelMeta(metric.metadata),\n        },\n      };\n    case 'llm_metrics':\n      // LiveKit's generation latency includes transport. First audio and completed\n      // playback are measured separately; a text token is not yet audible speech.\n      return {\n        name: `llm ttft ${ms(metric.ttftMs)}`,\n        data: {\n          ttftMs: metric.ttftMs,\n          durationMs: metric.durationMs,\n          tokensPerSecond: metric.tokensPerSecond,\n          promptTokens: metric.promptTokens,\n          completionTokens: metric.completionTokens,\n          totalTokens: metric.totalTokens,\n          cancelled: metric.cancelled,\n          ...modelMeta(metric.metadata),\n        },\n      };\n    case 'tts_metrics':\n      return {\n        name: `tts ttfb ${ms(metric.ttfbMs)}`,\n        data: {\n          ttfbMs: metric.ttfbMs,\n          durationMs: metric.durationMs,\n          audioDurationMs: metric.audioDurationMs,\n          charactersCount: metric.charactersCount,\n          cancelled: metric.cancelled,\n          streamed: metric.streamed,\n          ...modelMeta(metric.metadata),\n        },\n      };\n    case 'vad_metrics':\n      return {\n        name: 'vad',\n        data: {\n          idleTimeMs: metric.idleTimeMs,\n          inferenceCount: metric.inferenceCount,\n          inferenceDurationTotalMs: metric.inferenceDurationTotalMs,\n        },\n      };\n    case 'realtime_model_metrics':\n      return {\n        name: `realtime ttft ${ms(metric.ttftMs)}`,\n        data: {\n          ttftMs: metric.ttftMs,\n          durationMs: metric.durationMs,\n          tokensPerSecond: metric.tokensPerSecond,\n          promptTokens: metric.inputTokens,\n          completionTokens: metric.outputTokens,\n          totalTokens: metric.totalTokens,\n          ...modelMeta(metric.metadata),\n        },\n      };\n    default:\n      return undefined;\n  }\n}\n\n/**\n * Opens a `voice call` trace for one LiveKit session and bridges LiveKit's voice-pipeline\n * metrics into Mastra observability.\n *\n * The returned root span groups everything about the call: each conversation turn's Mastra\n * agent run (nested via {@link VoiceCallObservability.tracingContext}) plus an event span for\n * every STT, TTS, end-of-utterance, VAD, and LLM-latency metric LiveKit emits. Metrics are\n * point-in-time, so they're recorded as event spans (no duration) with the value in the name.\n * The root closes on the session `close` event with a per-model usage roll-up (token, character,\n * and audio totals for the whole call) from a `ModelUsageCollector`.\n *\n * Returns `undefined` when the Mastra instance has no observability configured, so callers\n * can treat instrumentation as a no-op without branching on config.\n */\nexport function startVoiceCallObservability(\n  options: VoiceCallObservabilityOptions,\n): VoiceCallObservability | undefined {\n  const span = getOrCreateSpan<SpanType.GENERIC>({\n    mastra: options.mastra,\n    type: SpanType.GENERIC,\n    name: 'voice call',\n    requestContext: options.requestContext,\n    metadata: {\n      agentId: options.agentId,\n      roomName: options.roomName,\n      ...(options.metadata.threadId ? { threadId: options.metadata.threadId } : {}),\n      ...(options.metadata.resourceId ? { resourceId: options.metadata.resourceId } : {}),\n    },\n  });\n\n  if (!span) return undefined;\n\n  const usage = new metrics.ModelUsageCollector();\n  let finalized = false;\n\n  const finalize = (opts?: { error?: unknown }): void => {\n    if (finalized) return;\n    finalized = true;\n    const summary = usage.flatten();\n    if (opts?.error) {\n      span.error({\n        error: opts.error instanceof Error ? opts.error : new Error(String(opts.error)),\n        metadata: { usage: summary },\n      });\n    } else {\n      span.end({ output: { usage: summary } });\n    }\n  };\n\n  return {\n    span,\n    tracingContext: { currentSpan: span },\n    recordTurn(metric) {\n      span.createEventSpan({ type: SpanType.GENERIC, name: `voice ${metric.phase} ${metric.outcome}`, output: metric });\n    },\n    attach(session: voice.AgentSession) {\n      session.on(voice.AgentSessionEventTypes.MetricsCollected, (event: { metrics: metrics.AgentMetrics }) => {\n        usage.collect(event.metrics);\n        const described = describeMetric(event.metrics);\n        if (!described) return;\n        // Event span: a point-in-time measurement, not a duration. The value is in the name\n        // (for the timeline) and the full fields are on output.\n        span.createEventSpan({\n          type: SpanType.GENERIC,\n          name: described.name,\n          output: { ...described.data, ...('speechId' in event.metrics ? { speechId: event.metrics.speechId } : {}) },\n        });\n      });\n      // The call ends when the session closes — finalize then so the root span and its usage\n      // roll-up land promptly. The worker's ctx.addShutdownCallback is a backstop for abnormal\n      // termination (the job ending without a clean session close).\n      session.on(voice.AgentSessionEventTypes.Close, (event: { error?: unknown }) => {\n        finalize({ error: event?.error ?? undefined });\n      });\n    },\n    finalize,\n  };\n}\n","import type { MastraDBMessage } from '@mastra/core/agent';\nimport type { MastraMemory } from '@mastra/core/memory';\n\nexport interface VoiceThreadArgs {\n  memory: MastraMemory;\n  threadId: string;\n  resourceId: string;\n}\n\n/**\n * Creates the call's memory thread up front when it doesn't exist yet, titled and tagged\n * so it reads as a voice call in Studio's thread list instead of an untitled thread that\n * only appears after the first exchange.\n */\nexport async function ensureVoiceCallThread({\n  memory,\n  threadId,\n  resourceId,\n  roomName,\n}: VoiceThreadArgs & { roomName: string }): Promise<void> {\n  const existing = await memory.getThreadById({ threadId });\n  if (existing) return;\n  await memory.createThread({\n    threadId,\n    resourceId,\n    title: 'Voice call',\n    metadata: { source: 'livekit', roomName },\n  });\n}\n\n/**\n * Persists the spoken greeting as an assistant message. The greeting is spoken via TTS\n * without going through the Mastra agent, so without this the saved thread would start\n * at the caller's first words instead of being a faithful call transcript.\n */\nexport async function persistSpokenGreeting({\n  memory,\n  threadId,\n  resourceId,\n  greeting,\n}: VoiceThreadArgs & { greeting: string }): Promise<void> {\n  // A whitespace/empty greeting would persist a low-value assistant message; skip it.\n  if (!greeting.trim()) return;\n  const message: MastraDBMessage = {\n    id: globalThis.crypto.randomUUID(),\n    role: 'assistant',\n    type: 'text',\n    createdAt: new Date(),\n    threadId,\n    resourceId,\n    content: {\n      format: 2,\n      parts: [{ type: 'text', text: greeting }],\n      metadata: { source: 'voice', kind: 'greeting' },\n    },\n  };\n  await memory.saveMessages({ messages: [message] });\n}\n","/**\n * Coordination between worker definition and worker startup. LiveKit plugins register\n * their inference runners at module-import time, and the AgentServer only spawns the\n * inference process for runners registered before it starts — so plugin imports kicked\n * off by `createLiveKitWorker()` must complete before `runLiveKitWorker()` boots the\n * server.\n */\nconst pendingSetup: Promise<unknown>[] = [];\nconst requestedEouMethods = new Set<string>();\n\nexport function queueWorkerSetup(setup: Promise<unknown>): void {\n  pendingSetup.push(setup);\n}\n\nexport function workerSetupComplete(): Promise<unknown> {\n  return Promise.allSettled(pendingSetup);\n}\n\nexport function requestEouMethod(method: string): void {\n  requestedEouMethods.add(method);\n}\n\nexport function isEouMethodRequested(method: string): boolean {\n  return requestedEouMethods.has(method);\n}\n","import { defineAgent, InferenceRunner, voice } from '@livekit/agents';\nimport type { JobContext, JobProcess, VAD } from '@livekit/agents';\nimport type { Agent as MastraAgent } from '@mastra/core/agent';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { MastraMemory } from '@mastra/core/memory';\nimport { RequestContext } from '@mastra/core/request-context';\nimport type { Workflow } from '@mastra/core/workflows';\nimport { createMastraVoiceAgent } from './bridge';\nimport type {\n  MastraVoiceAgent,\n  MastraVoiceAgentMemory,\n  VoiceReplyGenerator,\n  VoiceToolCall,\n  VoiceTurnCompleteHook,\n  VoiceTurnContext,\n} from './bridge';\nimport { parseSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { startVoiceCallObservability } from './observability';\nimport { observeVoiceSession } from './speech';\nimport type { VoiceSpeechCompleteHook, VoiceTurnMetricsHook } from './turn-metrics';\nimport { notifyVoiceObserver } from './turn-metrics';\nimport { ensureVoiceCallThread, persistSpokenGreeting } from './voice-thread';\nimport { isEouMethodRequested, queueWorkerSetup, requestEouMethod } from './worker-setup';\nimport { createWorkflowReplyGenerator } from './workflow-generator';\n\nconst EOU_METHODS = {\n  english: 'lk_end_of_utterance_en',\n  multilingual: 'lk_end_of_utterance_multilingual',\n} as const;\n\ninterface WorkerUserData {\n  vad?: VAD;\n  [key: string]: unknown;\n}\n\ntype TurnDetectionSetting = Exclude<voice.AgentSessionOptions['turnDetection'], undefined>;\n\nexport interface ResolveMastraAgentArgs {\n  metadata: LiveKitSessionMetadata;\n  ctx: JobContext;\n}\n\nexport interface SessionStartArgs {\n  session: voice.AgentSession;\n  ctx: JobContext;\n  agent: MastraVoiceAgent;\n  metadata: LiveKitSessionMetadata;\n}\n\n/** Context handed to {@link CreateLiveKitWorkerOptions.onCallEnd} when the call ends. */\nexport interface VoiceCallEndArgs {\n  /** Resolved memory mapping for the call (thread/resource), or `false` if memory was disabled. */\n  memory: MastraVoiceAgentMemory | false;\n  /**\n   * The `Memory` instance backing the call, or `null`. On the agent path it's the agent's\n   * (storage-equipped) memory; on the workflow/custom path it's the resolved `memoryInstance`. Use\n   * it for end-of-call maintenance — e.g. flush observational memory off the audio path.\n   */\n  memoryInstance: MastraMemory | null;\n  /** Dispatch metadata for the call. */\n  metadata: LiveKitSessionMetadata;\n  /** Request context for the call, if any. */\n  requestContext?: RequestContext;\n  /**\n   * The worker's `configuration` (greeting, consent requirements, …). Read it here to honor policy\n   * at end-of-call — e.g. only flush a call summary to observational memory when\n   * `configuration.consentPolicy.summaryStorage` isn't required, or the caller granted it.\n   */\n  configuration?: LiveKitWorkerConfiguration;\n  /** The LiveKit room name. */\n  roomName: string;\n  /** The LiveKit job context, for advanced teardown needs. */\n  ctx: JobContext;\n}\n\n/**\n * Fired once when the call ends. Runs off the audio path inside LiveKit's shutdown grace window,\n * and — unlike the per-turn {@link MastraVoiceAgentOptions.onTurnComplete} — it is AWAITED, so the\n * work completes before the worker process exits. A thrown error is logged, not propagated.\n */\nexport type VoiceCallEndHook = (args: VoiceCallEndArgs) => void | Promise<void>;\n\nexport interface CreateLiveKitWorkerOptions {\n  /** Generation and server-playout metrics; observer errors never interrupt the call. */\n  onTurnMetrics?: VoiceTurnMetricsHook;\n  /** Playback completion, separate from onTurnComplete. Does not reconcile memory. */\n  onSpeechComplete?: VoiceSpeechCompleteHook;\n  /** The Mastra instance whose agents handle voice sessions. */\n  mastra: Mastra;\n  /**\n   * Which Mastra agent answers each session: a fixed agent key/id, or a resolver called\n   * per session with the dispatch metadata. Defaults to `metadata.agentId`.\n   */\n  agent?: string | ((args: ResolveMastraAgentArgs) => string | MastraAgent | Promise<string | MastraAgent>);\n  /**\n   * Generate each turn's reply with a Mastra workflow instead of an agent: a `Workflow`\n   * instance, a fixed workflow key/id, or a resolver that returns a workflow id per session.\n   * The workflow runs once to completion per turn (LiveKit owns the turn boundary, so there is\n   * no suspend/resume). Mutually exclusive with `agent`; requires\n   * {@link CreateLiveKitWorkerOptions.workflowInput}.\n   */\n  workflow?: string | Workflow | ((args: ResolveMastraAgentArgs) => string | Promise<string>);\n  /**\n   * Maps a turn into the workflow's `inputData`. Required when `workflow` is set. A stateless\n   * mapping that passes the full transcript each turn avoids carrying conversation state in the\n   * workflow, e.g. `({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) })`.\n   */\n  workflowInput?: (args: VoiceTurnContext & { metadata: LiveKitSessionMetadata }) => unknown | Promise<unknown>;\n  /** Only stream text from this workflow step id. Defaults to every step that writes to its `writer`. */\n  replyStep?: string;\n  /** Fallback when the workflow streams no text via `writer`: derive the spoken reply from the final result. */\n  resultText?: (result: unknown) => string | undefined | void;\n  /** Lowest-level escape hatch: supply any reply generator directly (a custom workflow, remote bridge, …). */\n  generate?: VoiceReplyGenerator;\n  /**\n   * Speech-to-text: a LiveKit plugin instance or an inference model string like 'deepgram/nova-3'.\n   * For per-call / per-tenant selection, set the `configuration.stt` resolver — it takes\n   * precedence, with this option as the fallback.\n   */\n  stt?: voice.AgentSessionOptions['stt'];\n  /**\n   * Text-to-speech: a LiveKit plugin instance or an inference model string like 'cartesia/sonic-3'.\n   * For per-call / per-tenant selection, set the `configuration.tts` resolver — it takes\n   * precedence, with this option as the fallback.\n   */\n  tts?: voice.AgentSessionOptions['tts'];\n  /**\n   * Voice activity detection. `'silero'` (default) loads the Silero VAD from\n   * `@livekit/agents-plugin-silero`; pass an instance to bring your own, or `false` to disable.\n   */\n  vad?: VAD | 'silero' | false;\n  /**\n   * End-of-turn detection. `'multilingual'` or `'english'` load LiveKit's semantic turn\n   * detector model from `@livekit/agents-plugin-livekit`; other values are passed through\n   * (e.g. `'vad'`, `'stt'`, `'manual'`). To construct a `TurnDetector` instance per call (they\n   * need the LiveKit job context), use `configuration.turnDetection`; this option is its fallback.\n   */\n  turnDetection?: 'multilingual' | 'english' | TurnDetectionSetting;\n  /** Turn handling tuning (endpointing delays, interruption sensitivity, preemptive generation). */\n  turnHandling?: voice.AgentSessionOptions['turnHandling'];\n  /** Extra `AgentSession` options merged over what this helper builds. */\n  sessionOptions?: Partial<voice.AgentSessionOptions>;\n  /**\n   * Memory mapping. Defaults to `{ thread: metadata.threadId ?? room name, resource:\n   * metadata.resourceId ?? thread }` when the resolved agent has memory configured.\n   * Pass `false` to disable, or a function to customize.\n   */\n  memory?: false | ((args: ResolveMastraAgentArgs & { roomName: string }) => MastraVoiceAgentMemory | false);\n  /**\n   * The `Memory` instance used to bootstrap the call thread and persist the greeting on the\n   * workflow / custom-generator path, where there is no agent to source it from. Ignored on the\n   * agent path (the resolved agent's own memory is used). Pass the same `Memory` your workflow\n   * steps read and write through, so the saved thread is one faithful transcript (the up-front\n   * thread + the persisted greeting + every turn the steps write). If the `Memory` has no\n   * `storage` of its own, this worker's Mastra storage is injected into it (same as\n   * `agent.getMemory()` does), so a `Memory` that relies on the Mastra instance for storage works.\n   */\n  memoryInstance?:\n    MastraMemory | ((args: ResolveMastraAgentArgs) => MastraMemory | undefined | Promise<MastraMemory | undefined>);\n  /**\n   * Spoken while a Mastra tool call runs. Works on both the agent and workflow paths; on the\n   * workflow path it fires only for tool calls the reply step surfaces to its `writer` (use\n   * `pipeAgentReplyToWriter`). See {@link MastraVoiceAgentOptions.toolFeedback}.\n   */\n  toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void;\n  /**\n   * Fired once per turn after the reply has streamed to text-to-speech, off the audio path and\n   * fire-and-forget. Works on both the agent and workflow paths. Use it to fully background\n   * post-turn memory maintenance — e.g. a non-blocking `memory.updateWorkingMemory(...)` — so it\n   * never adds to the caller's latency. See {@link MastraVoiceAgentOptions.onTurnComplete}.\n   */\n  onTurnComplete?: VoiceTurnCompleteHook;\n  /**\n   * Fired once when the call ends (participant disconnects / job shuts down), via LiveKit's\n   * shutdown callback — entirely off the audio path, when latency no longer matters. Unlike\n   * `onTurnComplete`, it is AWAITED within LiveKit's shutdown grace window, so end-of-call work\n   * completes before the process exits. The ideal place to flush observational memory once for the\n   * whole call (instead of paying for it inline per turn). Keep it to a few seconds so it fits the\n   * grace window; a thrown error is logged, not propagated. See {@link VoiceCallEndArgs}.\n   */\n  onCallEnd?: VoiceCallEndHook;\n  /**\n   * Grouped conversation & compliance configuration. Related policy knobs live here — rather than\n   * as many top-level worker options — so this call stays flat as more are added. Today it carries\n   * the greeting / AI-disclosure; it's the intended home for further compliance controls (recording\n   * notice, periodic re-disclosure, data retention, human handoff, …). See\n   * {@link LiveKitWorkerConfiguration}.\n   */\n  configuration?: LiveKitWorkerConfiguration;\n  /**\n   * Static greeting spoken when the session starts.\n   *\n   * @deprecated Prefer `configuration.greeting.text`. Still honored for backwards compatibility;\n   * when `configuration.greeting` is also set, its fields take precedence.\n   */\n  greeting?: string;\n  /**\n   * Save the spoken greeting to the memory thread as an assistant message.\n   *\n   * @deprecated Prefer `configuration.greeting.persist`. Still honored for backwards compatibility.\n   */\n  persistGreeting?: boolean;\n  /**\n   * Voice-pipeline observability. When the Mastra instance has observability configured, each\n   * session opens a `voice call` trace: LiveKit's STT, TTS, end-of-utterance, VAD, and LLM\n   * latency metrics become child spans, and every turn's Mastra agent run nests under the call,\n   * which closes with a token/audio usage roll-up. Defaults to `true`; pass `false` to disable.\n   */\n  observability?: boolean;\n  inputOptions?: Parameters<voice.AgentSession['start']>[0]['inputOptions'];\n  outputOptions?: Parameters<voice.AgentSession['start']>[0]['outputOptions'];\n  /** Called after the session starts — attach event listeners, trigger replies, etc. */\n  onSessionStart?: (args: SessionStartArgs) => void | Promise<void>;\n}\n\nasync function loadSileroVad(): Promise<VAD> {\n  let silero;\n  try {\n    silero = await import('@livekit/agents-plugin-silero');\n  } catch (error) {\n    throw new Error(\n      \"@mastra/livekit: voice activity detection requires '@livekit/agents-plugin-silero'. \" +\n        'Install it, pass your own `vad` instance, or set `vad: false`.',\n      { cause: error },\n    );\n  }\n  return silero.VAD.load();\n}\n\nasync function loadTurnDetector(kind: 'multilingual' | 'english'): Promise<TurnDetectionSetting> {\n  let plugin;\n  try {\n    plugin = await import('@livekit/agents-plugin-livekit');\n  } catch (error) {\n    throw new Error(\n      `@mastra/livekit: turnDetection '${kind}' requires '@livekit/agents-plugin-livekit'. ` +\n        \"Install it or use a built-in mode like 'vad' or 'stt'.\",\n      { cause: error },\n    );\n  }\n  return kind === 'english'\n    ? (new plugin.turnDetector.EnglishModel() as TurnDetectionSetting)\n    : (new plugin.turnDetector.MultilingualModel() as TurnDetectionSetting);\n}\n\nasync function resolveMastraAgent(\n  options: CreateLiveKitWorkerOptions,\n  args: ResolveMastraAgentArgs,\n): Promise<MastraAgent> {\n  let ref: string | MastraAgent | undefined =\n    typeof options.agent === 'function' ? await options.agent(args) : options.agent;\n  ref ??= args.metadata.agentId;\n  if (!ref) {\n    throw new Error(\n      '@mastra/livekit: no Mastra agent specified. Set `agent` on createLiveKitWorker or pass ' +\n        '`agentId` in the dispatch metadata (e.g. via liveKitConnectionRoute).',\n    );\n  }\n  if (typeof ref !== 'string') return ref;\n  try {\n    return options.mastra.getAgentById(ref);\n  } catch {\n    return options.mastra.getAgent(ref);\n  }\n}\n\n// Returns the workflow boxed in an object: `Workflow` is thenable (it has a `.then()` builder\n// method), so a bare `Promise<Workflow>` return — or awaiting a `Workflow`-typed value — trips\n// TS's thenable checks and, at runtime, `await workflow` would call the builder's `.then`.\nasync function resolveWorkflow(\n  options: CreateLiveKitWorkerOptions,\n  args: ResolveMastraAgentArgs,\n): Promise<{ workflow: Workflow }> {\n  const resolver = options.workflow;\n  // The function form resolves to an id string (safe to await); a Workflow instance is only\n  // accepted as a direct value, never awaited.\n  const ref: string | Workflow = typeof resolver === 'function' ? await resolver(args) : (resolver ?? '');\n  if (!ref) {\n    throw new Error('@mastra/livekit: no workflow specified. Set `workflow` on createLiveKitWorker.');\n  }\n  if (typeof ref !== 'string') return { workflow: ref };\n  // getWorkflowById matches by workflow id and falls back to the registration key.\n  return { workflow: options.mastra.getWorkflowById(ref as never) as Workflow };\n}\n\nfunction resolveMemory(\n  options: CreateLiveKitWorkerOptions,\n  mastraAgent: MastraAgent | undefined,\n  args: ResolveMastraAgentArgs,\n  roomName: string,\n): MastraVoiceAgentMemory | false {\n  if (options.memory === false) return false;\n  if (typeof options.memory === 'function') return options.memory({ ...args, roomName });\n  // With an agent, default to memory only when the agent has its own. With a workflow (no\n  // agent), default the thread/resource mapping so the workflow input can use it.\n  if (mastraAgent && !mastraAgent.hasOwnMemory()) return false;\n  const thread = args.metadata.threadId ?? roomName;\n  return { thread, resource: args.metadata.resourceId ?? thread };\n}\n\n// The Memory instance backing thread bootstrap + greeting persistence. With an agent it comes\n// from the agent; on the workflow/custom-generator path there is no agent, so it comes from the\n// `memoryInstance` option (instance or resolver). Returns null when unavailable — bootstrap and\n// greeting persistence are then skipped and the generator owns persistence.\nexport async function resolveMemoryInstance(\n  options: Pick<CreateLiveKitWorkerOptions, 'memoryInstance' | 'mastra'>,\n  mastraAgent: MastraAgent | undefined,\n  args: ResolveMastraAgentArgs,\n  requestContext: RequestContext | undefined,\n): Promise<MastraMemory | null> {\n  if (mastraAgent) return (await mastraAgent.getMemory({ requestContext })) ?? null;\n  const resolver = options.memoryInstance;\n  if (!resolver) return null;\n  const instance = typeof resolver === 'function' ? await resolver(args) : resolver;\n  if (!instance) return null;\n  // Mirror Agent.getMemory: a Memory built without its own `storage` relies on the Mastra\n  // instance for one. The agent path gets that via getMemory(); on the workflow/custom-generator\n  // path we wire it here, so the worker's direct thread bootstrap + greeting persistence have a\n  // storage provider instead of throwing \"Memory requires a storage provider\".\n  instance.__registerMastra(options.mastra);\n  if (!instance.hasOwnStorage) {\n    const storage = options.mastra.getStorage();\n    if (storage) instance.setStorage(storage);\n  }\n  return instance;\n}\n\nexport function buildTurnHandling(\n  options: Pick<CreateLiveKitWorkerOptions, 'turnHandling'>,\n  turnDetection: TurnDetectionSetting | undefined,\n): voice.AgentSessionOptions['turnHandling'] {\n  return {\n    ...(turnDetection ? { turnDetection } : {}),\n    // Preemptive generation re-runs the Mastra agent on interim transcripts (up to 3\n    // times per turn), and every run persists to the memory thread — a discarded run\n    // leaves a partial user transcript and a partial, never-spoken reply behind. Off by\n    // default; opt back in via `turnHandling.preemptiveGeneration` if the latency win\n    // matters more than exact thread history, or pair it with `memory.options.readOnly`\n    // and persist committed turns yourself.\n    preemptiveGeneration: { enabled: false },\n    ...options.turnHandling,\n  };\n}\n\nasync function resolveInstructions(\n  mastraAgent: MastraAgent,\n  requestContext: RequestContext | undefined,\n): Promise<string | undefined> {\n  try {\n    const instructions = await mastraAgent.getInstructions({ requestContext });\n    return typeof instructions === 'string' ? instructions : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n/**\n * Per-call context handed to the `configuration` resolvers (greeting text, STT, TTS) so they can\n * key per-call / per-tenant behavior off the call. Built post-connect, so the dispatch metadata,\n * request context, and room name are all available.\n */\nexport interface VoiceCallContext {\n  /**\n   * Dispatch metadata for the call — the per-tenant routing signal (dialed number, tenant/agent id,\n   * thread/resource, and any `requestContext` entries) parsed from the LiveKit job metadata.\n   */\n  metadata: LiveKitSessionMetadata;\n  /** The request context built from the dispatch metadata, if any. */\n  requestContext?: RequestContext;\n  /** The LiveKit room name. */\n  roomName: string;\n  /** The LiveKit job context, for anything the metadata doesn't carry. */\n  ctx: JobContext;\n}\n\n/** Context handed to a greeting resolver. Alias of {@link VoiceCallContext}. */\nexport type GreetingContext = VoiceCallContext;\n\n/**\n * A per-call resolver for a session component (STT / TTS): invoked once per call (post-connect)\n * with the call {@link VoiceCallContext}. Return `undefined` to fall back to the static top-level\n * option for that component.\n */\nexport type SessionComponentResolver<T> = (\n  context: VoiceCallContext,\n) => T | undefined | void | Promise<T | undefined | void>;\n\n/**\n * The spoken greeting: a fixed string, or a resolver invoked once per call (post-connect) with the\n * call {@link GreetingContext}. Use the resolver form for a per-tenant greeting derived from the\n * dispatch metadata / request context — one multi-tenant agent, a different opening per tenant.\n * Return `undefined` (or empty) for no greeting on that call.\n */\nexport type GreetingText =\n  string | ((context: GreetingContext) => string | undefined | void | Promise<string | undefined | void>);\n\n/**\n * The opening greeting spoken when the session starts — and the vehicle for a required AI\n * disclosure. Under the EU AI Act (Art. 50) a person interacting with an AI system must be told so\n * at the first interaction; set `allowInterruptions: false` so that disclosure can't be talked over.\n */\nexport interface GreetingConfiguration {\n  /**\n   * What is spoken when the session starts. A fixed string, or a resolver invoked once per call\n   * (post-connect) with the call {@link GreetingContext} — return a per-tenant greeting built from\n   * the dispatch metadata / request context (e.g. `` `Thanks for calling ${tenant}, you're speaking\n   * with an AI assistant` ``). Omit, or return `undefined`, for no greeting.\n   */\n  text?: GreetingText;\n  /**\n   * Whether the caller can interrupt (barge over) the greeting. Defaults to LiveKit's behavior\n   * (interruptible). Set `false` to make the greeting play through — e.g. when it carries a\n   * disclosure the caller is legally required to hear (\"you're speaking with an AI assistant\"),\n   * so they can't talk over it and miss it.\n   */\n  allowInterruptions?: boolean;\n  /**\n   * Wait for the greeting to finish playing before continuing (greeting persistence and\n   * `onSessionStart`). Defaults to `false`. Pair with `allowInterruptions: false` when a\n   * disclosure must fully play out before any post-greeting work runs.\n   */\n  awaitPlayout?: boolean;\n  /**\n   * Save the spoken greeting to the memory thread as an assistant message, so the saved thread is\n   * a faithful call transcript. Defaults to `true`; only applies when memory is enabled.\n   */\n  persist?: boolean;\n  /**\n   * Periodically re-disclose the AI status on long calls (e.g. California SB 243). Once this many\n   * milliseconds have elapsed since the last disclosure, the next turn's reply is prefixed with a\n   * short reminder — spoken at the turn boundary, never mid-turn. Omit (or `0`) to disable. Example:\n   * `repeatEvery: 3 * 60_000` re-discloses roughly every three minutes.\n   *\n   * CAVEAT: don't combine with `turnHandling.preemptiveGeneration`. The reminder clock resets when\n   * the reminder is threaded into a reply stream; a speculative reply that gets discarded still\n   * consumes the interval, so the next real turn can miss its re-disclosure. (Preemptive\n   * generation is off by default and already incompatible with memory on this path.)\n   */\n  repeatEvery?: number;\n  /** The short reminder spoken on each re-disclosure. Defaults to a generic AI-assistant reminder. */\n  repeatText?: string;\n}\n\n/**\n * One consent item's requirement. `true` is shorthand for a required consent with defaults; the\n * object form carries the metadata a production consent record needs (whether it's required, and a\n * human-readable purpose for the spoken/logged prompt and the audit trail).\n */\nexport type ConsentRequirement =\n  | boolean\n  | {\n      /** Whether the caller's consent is required for this item. Defaults to `true` when present. */\n      required?: boolean;\n      /**\n       * Human-readable description of what is being consented to — for the spoken/logged consent\n       * prompt and the stored consent record. E.g. \"storing a summary of this call\".\n       */\n      purpose?: string;\n    };\n\n/**\n * The call's consent policy, as a **named, extensible set** — each item independently required\n * and independently granted at runtime, rather than one global \"consented\" flag. New consent\n * items are added as named keys so the model grows without reshaping existing ones or conflating\n * unrelated permissions (a production consent model, not one-size-fits-all). Declaring a policy\n * enforces nothing by itself: capture the caller's grant at runtime with `createConsentTool`, and\n * enforce it in your own code (at `onCallEnd`, or before any consent-gated action).\n */\nexport interface ConsentConfiguration {\n  /**\n   * Consent to store a summary of the call (observational-memory distillation, CRM notes, etc.).\n   * When required, persist a call summary only if the caller has granted this consent.\n   */\n  summaryStorage?: ConsentRequirement;\n  // Further consent items (recording, dataSharing, marketing, …) are added as named keys here.\n}\n\n/**\n * Agent-initiated hang-up: let the agent end the call itself (say goodbye → hang up). Enable it here\n * and add a matching tool to the agent with `createEndCallTool` (both default to the tool name\n * `'endCall'`). The tool only signals intent — from inside `agent.stream()` it can't reach the room;\n * the worker owns the hang-up. On each turn the worker watches for the tool, waits for the agent's\n * closing words to finish playing (so the goodbye is never cut off), then disconnects — running\n * `onCallEnd` on the way out, exactly as a caller-initiated hang-up does. Works on the agent and\n * workflow reply paths.\n */\nexport interface EndCallConfiguration {\n  /**\n   * Name of the tool the agent calls to end the call — must match the `id` you gave\n   * `createEndCallTool`. Defaults to `'endCall'`.\n   */\n  tool?: string;\n  /**\n   * Optional closing line spoken (non-interruptibly) right before hanging up, after the agent's own\n   * final words finish. Use it for a guaranteed sign-off or a compliance closing. Omit to just hang\n   * up once the agent's closing words finish playing.\n   */\n  message?: string;\n  /** Reason recorded on the shutdown (shows up in LiveKit logs). Defaults to `'agent ended call'`. */\n  reason?: string;\n  /**\n   * Extra drain time in milliseconds between the closing words finishing and the room being\n   * deleted. LiveKit's playout accounting is worker-local — when it reports the goodbye complete,\n   * the caller's client still holds a few hundred milliseconds of buffered audio (network +\n   * jitter buffer), and deleting the room immediately clips the tail of the goodbye mid-air.\n   * Defaults to {@link DEFAULT_END_CALL_DRAIN_MS}; set `0` to hang up the instant playout ends.\n   */\n  drainMs?: number;\n  /**\n   * Safety cap in milliseconds on how long to wait for the agent's closing words to finish playing\n   * before hanging up, in case the speaking state never clears. Defaults to `30000`.\n   */\n  maxWaitMs?: number;\n}\n\n/**\n * Grouped conversation & compliance configuration for {@link CreateLiveKitWorkerOptions.configuration}.\n * A single home for related policy knobs so they don't each become a top-level worker option. Today\n * it carries the greeting / AI-disclosure, consent requirements, agent-initiated hang-up, and\n * per-call STT/TTS selection; it's the intended landing spot for further compliance controls\n * (recording notice, data retention, …).\n */\nexport interface LiveKitWorkerConfiguration {\n  /** The opening greeting / AI-disclosure, plus optional periodic re-disclosure. See {@link GreetingConfiguration}. */\n  greeting?: GreetingConfiguration;\n  /**\n   * The call's consent policy. **Declarative only — the worker enforces nothing by itself.**\n   * Capture grants at runtime with `createConsentTool`; enforce them in your own code. See\n   * {@link ConsentConfiguration}.\n   */\n  consentPolicy?: ConsentConfiguration;\n  /** Let the agent end the call itself (say goodbye → hang up). See {@link EndCallConfiguration}. */\n  endCall?: EndCallConfiguration;\n  /**\n   * Per-call speech-to-text: a resolver invoked once per call (post-connect) with the call\n   * {@link VoiceCallContext}, returning anything the top-level `stt` option accepts (a plugin\n   * instance or an inference model string like `'deepgram/nova-3'`). Use it to pick the\n   * transcriber per tenant or language off the dispatch metadata / request context. Return\n   * `undefined` to fall back to the top-level `stt`. Runs during call setup, so keep it fast —\n   * when it constructs plugin instances, cache them across calls (e.g. a `Map` keyed by tenant).\n   */\n  stt?: SessionComponentResolver<voice.AgentSessionOptions['stt']>;\n  /**\n   * Per-call text-to-speech: a resolver invoked once per call (post-connect) with the call\n   * {@link VoiceCallContext}, returning anything the top-level `tts` option accepts (a plugin\n   * instance or an inference model string like `'cartesia/sonic-3'`). Use it to give each tenant\n   * its own voice / language. Return `undefined` to fall back to the top-level `tts`. Runs during\n   * call setup, so keep it fast — when it constructs plugin instances, cache them across calls.\n   */\n  tts?: SessionComponentResolver<voice.AgentSessionOptions['tts']>;\n  /**\n   * Per-call end-of-turn detection: a resolver invoked once per call (post-connect, inside the\n   * LiveKit job) with the call {@link VoiceCallContext}, returning anything the top-level\n   * `turnDetection` option accepts. LiveKit's `TurnDetector` instances read the job's inference\n   * executor from their constructor, so they can only be built here — not at module scope. Return\n   * `undefined` to fall back to the top-level `turnDetection`.\n   *\n   * The semantic turn detector's inference runners must register before the agent server boots,\n   * so when this resolver is set the worker eagerly imports `@livekit/agents-plugin-livekit`\n   * (if installed). Set the top-level `turnDetection` to `'multilingual'` or `'english'` to\n   * pre-register only that model; otherwise both are kept available.\n   */\n  turnDetection?: SessionComponentResolver<TurnDetectionSetting>;\n}\n\n/**\n * Resolves the effective greeting from the canonical `configuration.greeting` and the deprecated\n * top-level `greeting` / `persistGreeting` options. The legacy options are the base so existing\n * worker configs keep working unchanged; `configuration.greeting` overrides field-by-field. Exported\n * for unit testing.\n */\nexport function resolveGreetingConfig(\n  options: Pick<CreateLiveKitWorkerOptions, 'greeting' | 'persistGreeting' | 'configuration'>,\n): GreetingConfiguration {\n  return {\n    text: options.greeting,\n    persist: options.persistGreeting,\n    ...options.configuration?.greeting,\n  };\n}\n\n/**\n * Resolves the greeting text for a call: returns a fixed string as-is, or invokes the resolver form\n * with the call context to produce a per-tenant greeting. Empty / whitespace-nothing results\n * normalize to `undefined` (no greeting). Exported for unit testing.\n */\nexport async function resolveGreetingText(\n  text: GreetingText | undefined,\n  context: GreetingContext,\n): Promise<string | undefined> {\n  const resolved = typeof text === 'function' ? await text(context) : text;\n  return resolved || undefined;\n}\n\n/**\n * Resolves a per-call session component (STT / TTS): invokes the `configuration` resolver with the\n * call context, falling back to the static top-level option when there is no resolver or it\n * resolves to `undefined`. Exported for unit testing.\n */\nexport async function resolveSessionComponent<T>(\n  resolver: SessionComponentResolver<T> | undefined,\n  fallback: T | undefined,\n  context: VoiceCallContext,\n): Promise<T | undefined> {\n  const resolved = resolver ? await resolver(context) : undefined;\n  return resolved ?? fallback;\n}\n\n/**\n * Resolves this call's end-of-turn detection: the `configuration.turnDetection` resolver first,\n * then the static `turnDetection` option (loading the LiveKit semantic model for the\n * `'multilingual'` / `'english'` shorthands). Must run inside the job — LiveKit's turn detectors\n * read the job's inference executor when constructed. Exported for unit testing.\n */\nexport async function resolveTurnDetection(\n  options: Pick<CreateLiveKitWorkerOptions, 'turnDetection' | 'configuration'>,\n  context: VoiceCallContext,\n): Promise<TurnDetectionSetting | undefined> {\n  const resolved = await options.configuration?.turnDetection?.(context);\n  if (resolved) return resolved;\n  if (options.turnDetection === 'multilingual' || options.turnDetection === 'english') {\n    return loadTurnDetector(options.turnDetection);\n  }\n  return options.turnDetection;\n}\n\n/** A {@link GreetingConfiguration} whose `text` resolver has been resolved to a plain string. */\ntype ResolvedGreetingConfiguration = Omit<GreetingConfiguration, 'text'> & { text?: string };\n\n/**\n * Speaks the session's opening greeting, honoring the interruption / playout options. Takes the\n * already-resolved greeting (see {@link resolveGreetingText}) and returns the LiveKit `SpeechHandle`,\n * or `undefined` when there is no greeting text. Extracted and exported so the greeting behavior is\n * unit-testable without a live room.\n */\nexport async function speakGreeting(\n  session: Pick<voice.AgentSession, 'say'>,\n  greeting: ResolvedGreetingConfiguration,\n): Promise<ReturnType<voice.AgentSession['say']> | undefined> {\n  if (!greeting.text) return undefined;\n  // Only pass a say-options object when we actually override a default, so an unset\n  // `allowInterruptions` keeps LiveKit's own default rather than forcing a value.\n  const handle = session.say(\n    greeting.text,\n    greeting.allowInterruptions === undefined ? undefined : { allowInterruptions: greeting.allowInterruptions },\n  );\n  if (greeting.awaitPlayout) {\n    // waitForPlayout rejects if the greeting is interrupted; that's a normal outcome for an\n    // interruptible greeting, not a session-level failure, so swallow it.\n    await handle.waitForPlayout().catch(() => {});\n  }\n  return handle;\n}\n\n/** Default tool name the worker watches for to end the call. Matches `createEndCallTool`'s default. */\nexport const DEFAULT_END_CALL_TOOL = 'endCall';\n/** Default shutdown reason recorded when the agent ends the call. */\nexport const DEFAULT_END_CALL_REASON = 'agent ended call';\n/** Default safety cap on waiting for the agent's closing words to finish before hanging up. */\nexport const DEFAULT_END_CALL_MAX_WAIT_MS = 30_000;\n/**\n * Default post-playout drain before the room is deleted. LiveKit's \"playout completed\" is\n * worker-local; the caller's client still holds network + jitter-buffer audio, so hanging up the\n * instant the state clears clips the tail of the goodbye.\n */\nexport const DEFAULT_END_CALL_DRAIN_MS = 800;\n\n/** Agent states where the agent is still busy producing / playing a reply (not done speaking). */\nconst AGENT_BUSY_STATES = new Set<voice.AgentState>(['thinking', 'speaking']);\n\n/** The minimal logger surface these helpers use (the Mastra logger satisfies it). */\ntype WarnLogger = { warn: (message: string, ...args: unknown[]) => void };\n\n/**\n * Resolves once the agent is no longer producing or playing a reply — i.e. its state has left\n * `thinking`/`speaking` for `listening`/`idle`. Returns immediately when it's already idle. A\n * `maxWaitMs` safety cap guarantees it resolves even if the speaking state never clears. Used before\n * an agent-initiated hang-up so the closing words play out fully instead of being cut off by the\n * session close. Exported for unit testing.\n */\nexport function waitForAgentDoneSpeaking(\n  session: Pick<voice.AgentSession, 'agentState' | 'on' | 'off'>,\n  maxWaitMs: number = DEFAULT_END_CALL_MAX_WAIT_MS,\n): Promise<void> {\n  if (!AGENT_BUSY_STATES.has(session.agentState)) return Promise.resolve();\n  return new Promise<void>(resolve => {\n    // Check-then-subscribe with no await in between, so a state change can't slip past unobserved.\n    let timer: ReturnType<typeof setTimeout> | undefined;\n    const onChange = (ev: voice.AgentStateChangedEvent) => {\n      if (AGENT_BUSY_STATES.has(ev.newState)) return;\n      cleanup();\n      resolve();\n    };\n    const cleanup = () => {\n      session.off(voice.AgentSessionEventTypes.AgentStateChanged, onChange);\n      if (timer) clearTimeout(timer);\n    };\n    session.on(voice.AgentSessionEventTypes.AgentStateChanged, onChange);\n    timer = setTimeout(() => {\n      cleanup();\n      resolve();\n    }, maxWaitMs);\n    // Don't let the safety timer keep the worker process alive on its own.\n    (timer as { unref?: () => void }).unref?.();\n  });\n}\n\n/**\n * Ends the call after the agent asked to (via its end-call tool): wait for the agent's closing words\n * to finish, speak an optional final `message`, hold a short drain (`drainMs`) so audio buffered at\n * the caller finishes playing, then disconnect. The teardown's session close\n * force-interrupts any playing speech, so the waits here MUST complete before we disconnect — that's\n * the whole point of the sequence. `ctx.deleteRoom()` hangs up the caller (SIP-safe); `ctx.shutdown()`\n * ends the job and runs the registered shutdown callbacks (`onCallEnd`), so end-of-call work happens\n * exactly as it does on a caller hang-up. Both run in a `finally` so a hiccup while waiting still ends\n * the call. Never rejects — every step is guarded and failures are logged, so callers can safely\n * fire-and-forget it (`void runEndCall(...)`) from hooks. Exported for unit testing.\n */\nexport async function runEndCall(\n  session: Pick<voice.AgentSession, 'agentState' | 'on' | 'off' | 'say'>,\n  ctx: Pick<JobContext, 'deleteRoom' | 'shutdown'>,\n  config: EndCallConfiguration,\n  logger: WarnLogger,\n): Promise<void> {\n  try {\n    await waitForAgentDoneSpeaking(session, config.maxWaitMs ?? DEFAULT_END_CALL_MAX_WAIT_MS);\n    if (config.message) {\n      // waitForPlayout rejects if interrupted; the closing line is non-interruptible, but swallow\n      // anyway so a hiccup can't leave the call hanging.\n      await session\n        .say(config.message, { allowInterruptions: false })\n        .waitForPlayout()\n        .catch(() => {});\n    }\n    // Playout completion above is worker-local accounting: the caller's client still holds a few\n    // hundred milliseconds of buffered audio, and deleting the room now clips the goodbye mid-air.\n    // Let the buffer drain before tearing the room down.\n    const drainMs = config.drainMs ?? DEFAULT_END_CALL_DRAIN_MS;\n    if (drainMs > 0) await new Promise<void>(resolve => setTimeout(resolve, drainMs));\n  } catch (error) {\n    logger.warn('@mastra/livekit: waiting for the agent to finish before ending the call failed', error);\n  } finally {\n    try {\n      await ctx.deleteRoom();\n    } catch (error) {\n      logger.warn('@mastra/livekit: deleteRoom while ending the call failed', error);\n    }\n    try {\n      ctx.shutdown(config.reason ?? DEFAULT_END_CALL_REASON);\n    } catch (error) {\n      logger.warn('@mastra/livekit: shutdown while ending the call failed', error);\n    }\n  }\n}\n\n/**\n * Builds the per-turn hook that detects the agent's end-call tool and kicks off the hang-up once, or\n * `undefined` when end-call isn't configured. The detector fires the teardown fire-and-forget (the\n * turn never waits on it) and de-dupes so a repeated tool call can't start two hang-ups.\n */\nfunction buildEndCallDetector(\n  config: EndCallConfiguration | undefined,\n  getSession: () => Pick<voice.AgentSession, 'agentState' | 'on' | 'off' | 'say'> | undefined,\n  ctx: Pick<JobContext, 'deleteRoom' | 'shutdown'>,\n  logger: WarnLogger,\n): VoiceTurnCompleteHook | undefined {\n  if (!config) return undefined;\n  const toolName = config.tool ?? DEFAULT_END_CALL_TOOL;\n  let triggered = false;\n  return turnCtx => {\n    if (triggered) return;\n    if (!turnCtx.result.toolCalls.some(call => call.toolName === toolName)) return;\n    const session = getSession();\n    if (!session) return;\n    triggered = true;\n    void runEndCall(session, ctx, config, logger);\n  };\n}\n\n/** Runs the worker's own turn-complete detector alongside the user's `onTurnComplete`, if any. */\nfunction composeTurnComplete(\n  user: VoiceTurnCompleteHook | undefined,\n  detector: VoiceTurnCompleteHook | undefined,\n): VoiceTurnCompleteHook | undefined {\n  if (!detector) return user;\n  return ctx => {\n    // The detector is fire-and-forget (it kicks off the hang-up itself); don't couple the user's\n    // hook to it.\n    void detector(ctx);\n    return user?.(ctx);\n  };\n}\n\n/**\n * Builds a LiveKit agent worker definition that answers voice sessions with Mastra agents.\n *\n * Use as the default export of your worker entry file, then run it with the LiveKit\n * agents CLI:\n *\n * ```ts\n * // src/mastra/voice-worker.ts\n * import { fileURLToPath } from 'node:url';\n * import { createLiveKitWorker, runLiveKitWorker } from '@mastra/livekit/worker';\n * import { mastra } from './index';\n *\n * export default createLiveKitWorker({\n *   mastra,\n *   stt: 'deepgram/nova-3',\n *   tts: 'cartesia/sonic-3',\n *   turnDetection: 'multilingual',\n * });\n *\n * if (process.argv[1] === fileURLToPath(import.meta.url)) {\n *   runLiveKitWorker({ entry: import.meta.url, agentName: 'mastra-voice' });\n * }\n * ```\n */\nexport function createLiveKitWorker(options: CreateLiveKitWorkerOptions) {\n  if (options.generate && (options.agent || options.workflow)) {\n    throw new Error(\n      '@mastra/livekit: set exactly one reply generator — `generate`, `agent`, or `workflow` — not a combination.',\n    );\n  }\n  if (options.agent && options.workflow) {\n    throw new Error(\n      '@mastra/livekit: set `agent` or `workflow`, not both — they are mutually exclusive reply generators.',\n    );\n  }\n  if (options.workflow && !options.workflowInput) {\n    throw new Error(\n      '@mastra/livekit: `workflowInput` is required when `workflow` is set. Map the turn into the ' +\n        'workflow inputData, e.g. workflowInput: ({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) }).',\n    );\n  }\n  if (options.generate && options.configuration?.endCall) {\n    throw new Error(\n      '@mastra/livekit: `configuration.endCall` has no effect with `generate` — the worker cannot observe ' +\n        'tool calls from a custom reply generator. Detect the end-call tool inside your generator and call ' +\n        '`runEndCall` directly instead.',\n    );\n  }\n\n  const wantsSileroVad = options.vad === undefined || options.vad === 'silero';\n\n  // The turn detector's inference runners register at plugin-import time, and the agent\n  // server only spawns its inference process for runners registered before it starts —\n  // so begin the import now (worker definition happens at module scope, before\n  // runLiveKitWorker boots the server, which awaits this).\n  //\n  // A per-call `configuration.turnDetection` resolver builds its detector inside the job, so we\n  // can't know which model it wants: keep the runner(s) named by the static option, or both.\n  const hasTurnDetectionResolver = options.configuration?.turnDetection !== undefined;\n  if (options.turnDetection === 'multilingual' || options.turnDetection === 'english') {\n    requestEouMethod(EOU_METHODS[options.turnDetection]);\n  } else if (hasTurnDetectionResolver) {\n    for (const method of Object.values(EOU_METHODS)) {\n      requestEouMethod(method);\n    }\n  }\n  if (options.turnDetection === 'multilingual' || options.turnDetection === 'english' || hasTurnDetectionResolver) {\n    queueWorkerSetup(\n      import('@livekit/agents-plugin-livekit')\n        .then(() => {\n          // The plugin registers both language runners; keep only the requested ones so\n          // the inference process doesn't initialize (and require model files for) both.\n          for (const method of Object.values(EOU_METHODS)) {\n            if (!isEouMethodRequested(method)) {\n              delete InferenceRunner.registeredRunners[method];\n            }\n          }\n        })\n        .catch(() => {\n          // Surfaced with install guidance when the session actually needs it.\n        }),\n    );\n  }\n\n  return defineAgent<WorkerUserData>({\n    prewarm: async (proc: JobProcess<WorkerUserData>) => {\n      if (wantsSileroVad) {\n        proc.userData.vad = await loadSileroVad();\n      }\n    },\n    entry: async (ctx: JobContext<WorkerUserData>) => {\n      // Route degradation warnings through the Mastra logger so they honor the app's\n      // configured log level and transports instead of writing straight to the console.\n      const logger = options.mastra.getLogger();\n      const metadata = parseSessionMetadata(ctx.job.metadata);\n      const args: ResolveMastraAgentArgs = { metadata, ctx };\n      const requestContext = metadata.requestContext\n        ? new RequestContext<unknown>(Object.entries(metadata.requestContext))\n        : undefined;\n\n      // Agent-initiated hang-up: watch each turn for the end-call tool, then (once) wait for the\n      // agent's closing words to play out and disconnect. Composed with the user's onTurnComplete\n      // before the reply generator is built, because each path owns its hook differently: the\n      // workflow generator bakes it in at creation, the agent path threads it through the bridge.\n      // The closure reads `session`, assigned below before any turn runs.\n      const endCallDetector = buildEndCallDetector(options.configuration?.endCall, () => session, ctx, logger);\n      const onTurnComplete = composeTurnComplete(options.onTurnComplete, endCallDetector);\n\n      // Resolve the per-turn reply generator. Precedence: an explicit `generate` function, then\n      // a `workflow` (run-to-completion per turn), then a Mastra `agent` (the default).\n      let mastraAgent: MastraAgent | undefined;\n      let replyGenerator: VoiceReplyGenerator | undefined;\n      let agentLabel: string;\n      if (options.generate) {\n        replyGenerator = options.generate;\n        agentLabel = 'mastra-voice';\n      } else if (options.workflow) {\n        const { workflow } = await resolveWorkflow(options, args);\n        agentLabel = workflow.id;\n        const mapInput = options.workflowInput!;\n        replyGenerator = createWorkflowReplyGenerator({\n          workflow,\n          workflowInput: turnCtx => mapInput({ ...turnCtx, metadata }),\n          replyStep: options.replyStep,\n          resultText: options.resultText,\n          toolFeedback: options.toolFeedback,\n          onTurnComplete,\n        });\n      } else {\n        mastraAgent = await resolveMastraAgent(options, args);\n        agentLabel = mastraAgent.id ?? mastraAgent.name;\n      }\n\n      await ctx.connect();\n      const roomName = ctx.room.name ?? 'mastra-voice';\n\n      // LiveKit provisions its default VAD for undefined; null explicitly disables it.\n      let vad: VAD | null = null;\n      if (options.vad && options.vad !== 'silero') {\n        vad = options.vad;\n      } else if (wantsSileroVad) {\n        vad = ctx.proc.userData.vad ?? (await loadSileroVad());\n      }\n\n      const memory = resolveMemory(options, mastraAgent, args, roomName);\n      // The memory instance backs up-front thread bootstrap and greeting persistence. With an\n      // agent it comes from the agent; on the workflow/custom-generator path it comes from the\n      // `memoryInstance` option. When neither is available it stays null and the generator owns\n      // persistence (e.g. saveMessages inside a step).\n      const memoryInstance = memory ? await resolveMemoryInstance(options, mastraAgent, args, requestContext) : null;\n      if (memory && memoryInstance) {\n        try {\n          await ensureVoiceCallThread({\n            memory: memoryInstance,\n            threadId: memory.thread,\n            resourceId: memory.resource ?? memory.thread,\n            roomName,\n          });\n        } catch (error) {\n          logger.warn('@mastra/livekit: failed to create the voice call thread', error);\n        }\n      }\n\n      // One `voice call` trace per session: per-turn agent runs nest under it (via\n      // tracingContext below) and LiveKit's pipeline metrics attach as child spans. No-ops\n      // when the Mastra instance has no observability configured.\n      const voiceObs =\n        options.observability === false\n          ? undefined\n          : startVoiceCallObservability({\n              mastra: options.mastra,\n              agentId: agentLabel,\n              roomName,\n              metadata,\n              requestContext,\n            });\n      // Close the call span when the job ends, however it ends (registered up front so a\n      // failed start still finalizes); finalize() is idempotent.\n      if (voiceObs) {\n        ctx.addShutdownCallback(async () => {\n          voiceObs.finalize();\n        });\n      }\n\n      // End-of-call hook: runs after the caller hangs up, awaited within LiveKit's shutdown grace\n      // window so the work finishes before the process exits. Registered up front (like the\n      // observability finalizer) so it still fires if session start fails. Errors are logged.\n      if (options.onCallEnd) {\n        const onCallEnd = options.onCallEnd;\n        ctx.addShutdownCallback(async () => {\n          try {\n            await onCallEnd({\n              memory,\n              memoryInstance,\n              metadata,\n              requestContext,\n              configuration: options.configuration,\n              roomName,\n              ctx,\n            });\n          } catch (error) {\n            logger.warn('@mastra/livekit: onCallEnd hook threw', error);\n          }\n        });\n      }\n\n      // Resolve the greeting: `configuration.greeting` is canonical, but the deprecated top-level\n      // `greeting` / `persistGreeting` options are still honored (additive), configuration winning.\n      const greetingConfig = resolveGreetingConfig(options);\n      // Periodic AI re-disclosure (disabled unless a positive interval is set). Handled at the turn\n      // boundary inside the voice agent, so it works on the agent and workflow/custom paths alike.\n      const greetingReminder =\n        greetingConfig.repeatEvery && greetingConfig.repeatEvery > 0\n          ? { everyMs: greetingConfig.repeatEvery, text: greetingConfig.repeatText }\n          : undefined;\n\n      const onTurnMetrics: VoiceTurnMetricsHook = metric => {\n        voiceObs?.recordTurn(metric);\n        notifyVoiceObserver(options.onTurnMetrics, metric);\n      };\n      const agent = createMastraVoiceAgent({\n        onTurnMetrics,\n        ...(replyGenerator ? { generate: replyGenerator } : { agent: mastraAgent! }),\n        instructions: mastraAgent ? await resolveInstructions(mastraAgent, requestContext) : undefined,\n        memory,\n        requestContext,\n        toolFeedback: options.toolFeedback,\n        onTurnComplete,\n        greetingReminder,\n        streamOptions: voiceObs ? { tracingContext: voiceObs.tracingContext } : undefined,\n      });\n\n      // Per-call STT/TTS: the `configuration.stt` / `configuration.tts` resolvers pick this\n      // call's transcriber and voice (per-tenant voices/languages keyed off the dispatch\n      // metadata), falling back to the static top-level options. The same context feeds the\n      // greeting resolver below. Turn detection resolves here too: LiveKit's `TurnDetector`\n      // constructors read the job's inference executor, so instances must be built inside the job.\n      const callContext: VoiceCallContext = { metadata, requestContext, roomName, ctx };\n      const [stt, tts, turnDetection] = await Promise.all([\n        resolveSessionComponent(options.configuration?.stt, options.stt, callContext),\n        resolveSessionComponent(options.configuration?.tts, options.tts, callContext),\n        resolveTurnDetection(options, callContext),\n      ]);\n\n      const session = new voice.AgentSession({\n        stt,\n        tts,\n        vad,\n        turnHandling: buildTurnHandling(options, turnDetection),\n        ...options.sessionOptions,\n      });\n\n      // Subscribe before start so the first turn's metrics are captured.\n      voiceObs?.attach(session);\n      const detachSpeechObserver = observeVoiceSession(session, {\n        onTurnMetrics,\n        onSpeechComplete: options.onSpeechComplete,\n      });\n      ctx.addShutdownCallback(async () => {\n        detachSpeechObserver();\n      });\n\n      try {\n        await session.start({\n          agent,\n          room: ctx.room,\n          inputOptions: options.inputOptions,\n          outputOptions: options.outputOptions,\n        });\n\n        if (greetingConfig.text) {\n          // Resolve the greeting once (a fixed string, or a per-tenant resolver run post-connect),\n          // then use that same resolved text for both speaking and persistence.\n          const greetingText = await resolveGreetingText(greetingConfig.text, callContext);\n          if (greetingText) {\n            await speakGreeting(session, { ...greetingConfig, text: greetingText });\n            if (greetingConfig.persist !== false && memory && memoryInstance) {\n              try {\n                await persistSpokenGreeting({\n                  memory: memoryInstance,\n                  threadId: memory.thread,\n                  resourceId: memory.resource ?? memory.thread,\n                  greeting: greetingText,\n                });\n              } catch (error) {\n                logger.warn('@mastra/livekit: failed to persist the greeting', error);\n              }\n            }\n          }\n        }\n        await options.onSessionStart?.({ session, ctx, agent, metadata });\n      } catch (error) {\n        detachSpeechObserver();\n        voiceObs?.finalize({ error });\n        throw error;\n      }\n    },\n  });\n}\n","import { fileURLToPath } from 'node:url';\nimport { cli, ServerOptions } from '@livekit/agents';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { workerSetupComplete } from './worker-setup';\n\nexport interface RunLiveKitWorkerOptions {\n  /** The worker entry module whose default export is the agent definition. Pass `import.meta.url`. */\n  entry: string | URL;\n  /** LiveKit agent name for explicit dispatch. Defaults to `'mastra-voice'`. */\n  agentName?: string;\n  /** Extra LiveKit ServerOptions merged over what this helper builds. */\n  serverOptions?: Partial<ConstructorParameters<typeof ServerOptions>[0]>;\n}\n\nexport function resolveWorkerEntryPath(entry: string | URL): string {\n  if (entry instanceof URL) return fileURLToPath(entry);\n  return entry.startsWith('file:') ? fileURLToPath(entry) : entry;\n}\n\n/**\n * Starts the LiveKit agent worker CLI (`dev` / `start` / `connect` subcommands) for a\n * worker entry file. Call it from the same file that default-exports\n * {@link createLiveKitWorker}, guarded so it only runs when executed directly:\n *\n * ```ts\n * import { fileURLToPath } from 'node:url';\n * import { createLiveKitWorker, runLiveKitWorker } from '@mastra/livekit/worker';\n * import { mastra } from './index';\n *\n * export default createLiveKitWorker({ mastra, agent: 'support' });\n *\n * if (process.argv[1] === fileURLToPath(import.meta.url)) {\n *   runLiveKitWorker({ entry: import.meta.url, agentName: 'mastra-voice' });\n * }\n * ```\n *\n * Using this helper (instead of `cli.runApp` from `@livekit/agents`) guarantees the worker\n * runtime and the bridge share one copy of the LiveKit SDK.\n */\nexport function runLiveKitWorker(options: RunLiveKitWorkerOptions): void {\n  // Wait for plugin imports queued by createLiveKitWorker so their inference runners\n  // (e.g. the turn detector) are registered before the agent server starts.\n  void workerSetupComplete().then(() => {\n    cli.runApp(\n      new ServerOptions({\n        agent: resolveWorkerEntryPath(options.entry),\n        agentName: options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME,\n        ...options.serverOptions,\n      }),\n    );\n  });\n}\n"],"mappings":"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"}