@mastra/livekit 0.3.1-alpha.2 → 0.3.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"worker-entry.js","names":[],"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';\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 /** 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 view of the reply latency (includes transport), distinct from the Mastra\n // agent-run model span: time-to-first-token is what the caller actually waits for.\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 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({ type: SpanType.GENERIC, name: described.name, output: described.data });\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 { randomUUID } from 'node:crypto';\nimport 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: 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 { 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 /** 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'`).\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\n | ((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 the user message to the memory thread —\n // duplicating and even saving partial transcripts. Off by default; opt back in via\n // `turnHandling.preemptiveGeneration` if the latency win matters more than exact\n // thread history.\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\n | ((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\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/** 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 if (options.turnDetection === 'multilingual' || options.turnDetection === 'english') {\n requestEouMethod(EOU_METHODS[options.turnDetection]);\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 let vad: VAD | undefined;\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 let turnDetection: TurnDetectionSetting | undefined;\n if (options.turnDetection === 'multilingual' || options.turnDetection === 'english') {\n turnDetection = await loadTurnDetector(options.turnDetection);\n } else {\n turnDetection = options.turnDetection;\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 agent = createMastraVoiceAgent({\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.\n const callContext: VoiceCallContext = { metadata, requestContext, roomName, ctx };\n const [stt, tts] = await Promise.all([\n resolveSessionComponent(options.configuration?.stt, options.stt, callContext),\n resolveSessionComponent(options.configuration?.tts, options.tts, 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\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 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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"}
1
+ {"version":3,"file":"worker-entry.js","names":[],"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';\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 /** 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 view of the reply latency (includes transport), distinct from the Mastra\n // agent-run model span: time-to-first-token is what the caller actually waits for.\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 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({ type: SpanType.GENERIC, name: described.name, output: described.data });\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 { randomUUID } from 'node:crypto';\nimport 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: 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 { 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 /** 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'`).\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\n | ((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\n | ((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\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/** 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 if (options.turnDetection === 'multilingual' || options.turnDetection === 'english') {\n requestEouMethod(EOU_METHODS[options.turnDetection]);\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 let vad: VAD | undefined;\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 let turnDetection: TurnDetectionSetting | undefined;\n if (options.turnDetection === 'multilingual' || options.turnDetection === 'english') {\n turnDetection = await loadTurnDetector(options.turnDetection);\n } else {\n turnDetection = options.turnDetection;\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 agent = createMastraVoiceAgent({\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.\n const callContext: VoiceCallContext = { metadata, requestContext, roomName, ctx };\n const [stt, tts] = await Promise.all([\n resolveSessionComponent(options.configuration?.stt, options.stt, callContext),\n resolveSessionComponent(options.configuration?.tts, options.tts, 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\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 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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAc,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAWzD,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,KAAK,oBAAoB,GAAG,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,eAAe,CAAC,EAAE,SAAS,CAAC,CAAC;AAE3F,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,GAAG,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC;IAC5B,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,sBAAsB,CAAC;CAClC;AAED,yFAAyF;AACzF,MAAM,WAAW,gBAAgB;IAC/B,iGAAiG;IACjG,MAAM,EAAE,sBAAsB,GAAG,KAAK,CAAC;IACvC;;;;OAIG;IACH,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,sCAAsC;IACtC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,4CAA4C;IAC5C,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;;OAIG;IACH,aAAa,CAAC,EAAE,0BAA0B,CAAC;IAC3C,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,GAAG,EAAE,UAAU,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhF,MAAM,WAAW,0BAA0B;IACzC,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,sBAAsB,KAAK,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC;IAC1G;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC,IAAI,EAAE,sBAAsB,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5F;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,GAAG;QAAE,QAAQ,EAAE,sBAAsB,CAAA;KAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9G,uGAAuG;IACvG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8GAA8G;IAC9G,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC5D,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B;;;;OAIG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACvC;;;;OAIG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACvC;;;OAGG;IACH,GAAG,CAAC,EAAE,GAAG,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,cAAc,GAAG,SAAS,GAAG,oBAAoB,CAAC;IAClE,kGAAkG;IAClG,YAAY,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC;IACzD,wEAAwE;IACxE,cAAc,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACpD;;;;OAIG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,sBAAsB,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,sBAAsB,GAAG,KAAK,CAAC,CAAC;IAC3G;;;;;;;;OAQG;IACH,cAAc,CAAC,EACX,YAAY,GACZ,CAAC,CAAC,IAAI,EAAE,sBAAsB,KAAK,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC;IACrG;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACtE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,0BAA0B,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAC1E,aAAa,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAC5E,sFAAsF;IACtF,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AA2FD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,GAAG,QAAQ,CAAC,EACtE,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,cAAc,GAAG,SAAS,GACzC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAgB9B;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,0BAA0B,EAAE,cAAc,CAAC,EACzD,aAAa,EAAE,oBAAoB,GAAG,SAAS,GAC9C,KAAK,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAY3C;AAcD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,EAAE,sBAAsB,CAAC;IACjC,oEAAoE;IACpE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,GAAG,EAAE,UAAU,CAAC;CACjB;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI,CACxC,OAAO,EAAE,gBAAgB,KACtB,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;AAEnG;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP;IACE,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC;CAErC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oGAAoG;IACpG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,qHAAqH;IACrH,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC;;;;OAIG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,mGAAmG;IACnG,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,wBAAwB,CAAC,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IACjE;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,wBAAwB,CAAC,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CAAC,0BAA0B,EAAE,UAAU,GAAG,iBAAiB,GAAG,eAAe,CAAC,GAC1F,qBAAqB,CAMvB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG7B;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,QAAQ,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAAG,SAAS,EACjD,QAAQ,EAAE,CAAC,GAAG,SAAS,EACvB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAGxB;AAED,iGAAiG;AACjG,KAAK,6BAA6B,GAAG,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,EACxC,QAAQ,EAAE,6BAA6B,GACtC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAc5D;AAED,uGAAuG;AACvG,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAC/C,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAC1D,+FAA+F;AAC/F,eAAO,MAAM,4BAA4B,QAAS,CAAC;AACnD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAK7C,qFAAqF;AACrF,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,CAAC;AAE1E;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC,EAC9D,SAAS,GAAE,MAAqC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,EACtE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,YAAY,GAAG,UAAU,CAAC,EAChD,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAwCD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,6DA+PtE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/livekit",
3
- "version": "0.3.1-alpha.2",
3
+ "version": "0.3.2-alpha.0",
4
4
  "description": "LiveKit voice integration for Mastra agents — realtime voice with semantic turn detection and barge-in",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -92,9 +92,9 @@
92
92
  "typescript": "^7.0.2",
93
93
  "vitest": "4.1.10",
94
94
  "zod": "^4.4.3",
95
- "@internal/lint": "0.0.129",
96
- "@internal/types-builder": "0.0.104",
97
- "@mastra/core": "1.64.0-alpha.7"
95
+ "@internal/lint": "0.0.130",
96
+ "@internal/types-builder": "0.0.105",
97
+ "@mastra/core": "1.65.0-alpha.3"
98
98
  },
99
99
  "scripts": {
100
100
  "build:lib": "tsdown --silent --config tsdown.config.ts",
@@ -1 +0,0 @@
1
- {"version":3,"file":"remote-BZ7eyB1q.cjs","names":["ReadableStream","RequestContext","llm","voice","RequestContext","ReadableStream","APIStatusError","APIConnectionError","APITimeoutError","APIError"],"sources":["../src/messages.ts","../src/bridge.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","import { ReadableStream } from 'node:stream/web';\nimport { llm, voice } from '@livekit/agents';\nimport type { Agent as MastraAgent, AgentExecutionOptionsBase } 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';\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(source: ReadableStream<string>, text: string): ReadableStream<string> {\n const prefix = text.endsWith(' ') ? text : `${text} `;\n const reader = source.getReader();\n return new ReadableStream<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\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 /**\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<string> | null | Promise<ReadableStream<string> | 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 the reply has streamed: off the audio path (the caller already heard\n // the text), 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<string>({\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 === '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 // 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) controller.enqueue(filler.endsWith(' ') ? filler : `${filler} `);\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 /**\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 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.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> | null> {\n const messages: VoiceTurnMessage[] =\n this.memory === false ? chatContextToMessages(chatCtx) : extractNewTurnMessages(chatCtx);\n if (messages.length === 0) return null;\n\n const reply = await this.replyGenerator({\n messages,\n chatCtx,\n memory: this.memory,\n requestContext: this.requestContext,\n tracingContext: this.streamOptions?.tracingContext,\n });\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) return reply;\n this.reminder?.markDelivered();\n return prependText(reply, reminder);\n }\n}\n\nexport function createMastraVoiceAgent(options: MastraVoiceAgentOptions): MastraVoiceAgent {\n return new MastraVoiceAgent(options);\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';\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 ? { thread: ctx.memory.thread, resource: ctx.memory.resource ?? ctx.memory.thread }\n : undefined,\n requestContext: serializeRequestContext(ctx.requestContext),\n ...extraBody,\n };\n\n return new ReadableStream<string>({\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 '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 // 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) controller.enqueue(filler.endsWith(' ') ? filler : `${filler} `);\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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"}