@mastra/livekit 0.3.0 → 0.3.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -4
- package/README.md +1 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/index.cjs +172 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +168 -121
- package/dist/index.js.map +1 -1
- package/dist/llm-plugin.d.ts.map +1 -1
- package/dist/plugin-entry.cjs +199 -172
- package/dist/plugin-entry.cjs.map +1 -1
- package/dist/plugin-entry.js +195 -165
- package/dist/plugin-entry.js.map +1 -1
- package/dist/remote-BZ7eyB1q.cjs +664 -0
- package/dist/remote-BZ7eyB1q.cjs.map +1 -0
- package/dist/remote-D7n50m8S.js +629 -0
- package/dist/remote-D7n50m8S.js.map +1 -0
- package/dist/worker-entry.cjs +623 -538
- package/dist/worker-entry.cjs.map +1 -1
- package/dist/worker-entry.d.ts +2 -1
- package/dist/worker-entry.d.ts.map +1 -1
- package/dist/worker-entry.js +618 -529
- package/dist/worker-entry.js.map +1 -1
- package/dist/workflow-generator-B67QrY8L.cjs +209 -0
- package/dist/workflow-generator-B67QrY8L.cjs.map +1 -0
- package/dist/workflow-generator-BtfClQcM.js +180 -0
- package/dist/workflow-generator-BtfClQcM.js.map +1 -0
- package/package.json +14 -14
- package/CHANGELOG.md +0 -426
- package/dist/chunk-2E3MTAOA.js +0 -133
- package/dist/chunk-2E3MTAOA.js.map +0 -1
- package/dist/chunk-4O7IN74Y.js +0 -568
- package/dist/chunk-4O7IN74Y.js.map +0 -1
- package/dist/chunk-DBVKNDAQ.cjs +0 -574
- package/dist/chunk-DBVKNDAQ.cjs.map +0 -1
- package/dist/chunk-MWTEZOBS.cjs +0 -139
- package/dist/chunk-MWTEZOBS.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/observability.ts","../src/voice-thread.ts","../src/worker-setup.ts","../src/worker.ts","../src/run.ts"],"names":["getOrCreateSpan","SpanType","metrics","voice","randomUUID","InferenceRunner","defineAgent","parseSessionMetadata","requestContext","RequestContext","createWorkflowReplyGenerator","createMastraVoiceAgent","fileURLToPath","cli","ServerOptions","DEFAULT_LIVEKIT_AGENT_NAME"],"mappings":"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","file":"worker-entry.cjs","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"]}
|
|
1
|
+
{"version":3,"file":"worker-entry.cjs","names":["SpanType","metrics","voice","voice","InferenceRunner","parseSessionMetadata","RequestContext","createWorkflowReplyGenerator","createMastraVoiceAgent","ServerOptions"],"sources":["../src/observability.ts","../src/voice-thread.ts","../src/worker-setup.ts","../src/worker.ts","../src/run.ts"],"sourcesContent":["import { metrics, voice } from '@livekit/agents';\nimport type { Mastra } from '@mastra/core/mastra';\nimport { getOrCreateSpan, SpanType } from '@mastra/core/observability';\nimport type { Span, TracingContext } from '@mastra/core/observability';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { LiveKitSessionMetadata } from './metadata';\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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"}
|
package/dist/worker-entry.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ export { runLiveKitWorker } from './run.js';
|
|
|
4
4
|
export type { RunLiveKitWorkerOptions } from './run.js';
|
|
5
5
|
export { chatContextToMessages } from './messages.js';
|
|
6
6
|
export type { VoiceTurnMessage } from './messages.js';
|
|
7
|
-
export
|
|
7
|
+
export { MastraVoiceAgent, createMastraVoiceAgent } from './bridge.js';
|
|
8
|
+
export type { MastraStreamOptions, MastraVoiceAgentMemory, MastraVoiceAgentOptions, VoiceReplyGenerator, VoiceToolCall, VoiceTurnCompleteContext, VoiceTurnCompleteHook, VoiceTurnContext, VoiceTurnResult, VoiceTurnUsage, } from './bridge.js';
|
|
8
9
|
export { createRemoteAgentReplyGenerator } from './remote.js';
|
|
9
10
|
export type { RemoteMastraAgentOptions, RemoteAgentReplyGeneratorOptions } from './remote.js';
|
|
10
11
|
//# sourceMappingURL=worker-entry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-entry.d.ts","sourceRoot":"","sources":["../src/worker-entry.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EAInB,aAAa,EACb,wBAAwB,EACxB,UAAU,EACV,qBAAqB,EACrB,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,YAAY,EAAE,uBAAuB,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"worker-entry.d.ts","sourceRoot":"","sources":["../src/worker-entry.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EAInB,aAAa,EACb,wBAAwB,EACxB,UAAU,EACV,qBAAqB,EACrB,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,YAAY,EAAE,uBAAuB,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACpE,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,aAAa,EACb,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAE,+BAA+B,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,UAAU,CAAC"}
|