@mastra/livekit 0.0.0-aggregate-trace-20261015182851

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.
Files changed (83) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +48 -0
  3. package/dist/bridge.d.ts +256 -0
  4. package/dist/bridge.d.ts.map +1 -0
  5. package/dist/client-entry.cjs +17 -0
  6. package/dist/client-entry.cjs.map +1 -0
  7. package/dist/client-entry.d.ts +8 -0
  8. package/dist/client-entry.d.ts.map +1 -0
  9. package/dist/client-entry.js +16 -0
  10. package/dist/client-entry.js.map +1 -0
  11. package/dist/consent.d.ts +55 -0
  12. package/dist/consent.d.ts.map +1 -0
  13. package/dist/constants.d.ts +3 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/dispatch.d.ts +26 -0
  16. package/dist/dispatch.d.ts.map +1 -0
  17. package/dist/end-call.d.ts +50 -0
  18. package/dist/end-call.d.ts.map +1 -0
  19. package/dist/index.cjs +290 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.ts +20 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +281 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/llm-plugin.d.ts +117 -0
  26. package/dist/llm-plugin.d.ts.map +1 -0
  27. package/dist/messages.d.ts +50 -0
  28. package/dist/messages.d.ts.map +1 -0
  29. package/dist/metadata.d.ts +17 -0
  30. package/dist/metadata.d.ts.map +1 -0
  31. package/dist/observability.d.ts +49 -0
  32. package/dist/observability.d.ts.map +1 -0
  33. package/dist/plugin-entry.cjs +222 -0
  34. package/dist/plugin-entry.cjs.map +1 -0
  35. package/dist/plugin-entry.d.ts +10 -0
  36. package/dist/plugin-entry.d.ts.map +1 -0
  37. package/dist/plugin-entry.js +217 -0
  38. package/dist/plugin-entry.js.map +1 -0
  39. package/dist/recording-route.d.ts +31 -0
  40. package/dist/recording-route.d.ts.map +1 -0
  41. package/dist/recording-types.d.ts +11 -0
  42. package/dist/recording-types.d.ts.map +1 -0
  43. package/dist/recording.d.ts +21 -0
  44. package/dist/recording.d.ts.map +1 -0
  45. package/dist/remote-CIMCGicy.js +822 -0
  46. package/dist/remote-CIMCGicy.js.map +1 -0
  47. package/dist/remote-GSOxa--f.cjs +875 -0
  48. package/dist/remote-GSOxa--f.cjs.map +1 -0
  49. package/dist/remote.d.ts +68 -0
  50. package/dist/remote.d.ts.map +1 -0
  51. package/dist/routes.d.ts +66 -0
  52. package/dist/routes.d.ts.map +1 -0
  53. package/dist/run.d.ts +32 -0
  54. package/dist/run.d.ts.map +1 -0
  55. package/dist/speech.d.ts +19 -0
  56. package/dist/speech.d.ts.map +1 -0
  57. package/dist/turn-metrics-CqqzdLDe.js +53 -0
  58. package/dist/turn-metrics-CqqzdLDe.js.map +1 -0
  59. package/dist/turn-metrics-CtVHHdJj.cjs +82 -0
  60. package/dist/turn-metrics-CtVHHdJj.cjs.map +1 -0
  61. package/dist/turn-metrics.d.ts +69 -0
  62. package/dist/turn-metrics.d.ts.map +1 -0
  63. package/dist/voice-stream.d.ts +3 -0
  64. package/dist/voice-stream.d.ts.map +1 -0
  65. package/dist/voice-thread.d.ts +23 -0
  66. package/dist/voice-thread.d.ts.map +1 -0
  67. package/dist/worker-entry.cjs +727 -0
  68. package/dist/worker-entry.cjs.map +1 -0
  69. package/dist/worker-entry.d.ts +15 -0
  70. package/dist/worker-entry.d.ts.map +1 -0
  71. package/dist/worker-entry.js +711 -0
  72. package/dist/worker-entry.js.map +1 -0
  73. package/dist/worker-setup.d.ts +5 -0
  74. package/dist/worker-setup.d.ts.map +1 -0
  75. package/dist/worker.d.ts +487 -0
  76. package/dist/worker.d.ts.map +1 -0
  77. package/dist/workflow-generator-5nHqlZV7.js +210 -0
  78. package/dist/workflow-generator-5nHqlZV7.js.map +1 -0
  79. package/dist/workflow-generator-D67FvbFe.cjs +239 -0
  80. package/dist/workflow-generator-D67FvbFe.cjs.map +1 -0
  81. package/dist/workflow-generator.d.ts +92 -0
  82. package/dist/workflow-generator.d.ts.map +1 -0
  83. package/package.json +121 -0
@@ -0,0 +1,222 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_turn_metrics = require("./turn-metrics-CtVHHdJj.cjs");
3
+ const require_remote = require("./remote-GSOxa--f.cjs");
4
+ let _livekit_agents = require("@livekit/agents");
5
+ let _mastra_core_request_context = require("@mastra/core/request-context");
6
+ //#region src/llm-plugin.ts
7
+ function toRequestContext(value) {
8
+ if (!value) return void 0;
9
+ if (value instanceof _mastra_core_request_context.RequestContext) return value;
10
+ return new _mastra_core_request_context.RequestContext(Object.entries(value));
11
+ }
12
+ function mapUsageToLiveKit(usage) {
13
+ return {
14
+ completionTokens: usage.completionTokens,
15
+ promptTokens: usage.promptTokens,
16
+ promptCachedTokens: usage.promptCachedTokens,
17
+ totalTokens: usage.totalTokens
18
+ };
19
+ }
20
+ /**
21
+ * Tool names carried by a `toolCtx`, across @livekit/agents versions: 1.5+ always passes a
22
+ * `ToolContext` class instance (tools behind getters, `Object.keys` sees only private fields),
23
+ * while 1.4 and the object shorthand pass a plain name→tool map.
24
+ */
25
+ function livekitToolNames(toolCtx) {
26
+ const instance = toolCtx;
27
+ if (typeof instance.flatten === "function") return instance.flatten().map((tool) => {
28
+ const { id, name } = tool;
29
+ return id ?? name ?? "unknown";
30
+ });
31
+ return Object.keys(toolCtx);
32
+ }
33
+ /**
34
+ * A standard LiveKit LLM plugin (`llm.LLM`) backed by a Mastra agent. Drop it into the `llm` slot of a
35
+ * customer-owned `voice.AgentSession` and the Mastra app (agent loop, tools, memory, observability)
36
+ * runs wherever it's deployed — most importantly on a **remote** Mastra server reached over HTTP.
37
+ *
38
+ * Tools are defined and executed **server-side** on the Mastra agent; LiveKit-side `toolCtx` is
39
+ * ignored (with a one-time warning). Tool activity surfaces via `toolFeedback` (spoken) and
40
+ * `onToolCall` / `onTurnComplete` (programmatic). `voice.Agent` instructions do **not** reach the
41
+ * Mastra agent — put instructions on the Mastra agent instead.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const session = new voice.AgentSession({
46
+ * stt: 'deepgram/nova-3',
47
+ * tts: 'cartesia/sonic-3',
48
+ * llm: new MastraLLM({
49
+ * remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'callCenter' },
50
+ * memory: { thread: callId, resource: callerId },
51
+ * }),
52
+ * });
53
+ * ```
54
+ */
55
+ var MastraLLM = class extends _livekit_agents.llm.LLM {
56
+ #model;
57
+ #onTurnMetrics;
58
+ #memory;
59
+ #requestContext;
60
+ /** Non-remote sources are built once; remote is built per turn so it can pick up `connOptions.timeoutMs`. */
61
+ #staticGenerator;
62
+ #remoteOptions;
63
+ #warnedToolCtx = false;
64
+ constructor(options) {
65
+ super();
66
+ this.#onTurnMetrics = options.onTurnMetrics;
67
+ const sources = [
68
+ options.remote ? "remote" : void 0,
69
+ options.agent ? "agent" : void 0,
70
+ options.generate ? "generate" : void 0
71
+ ].filter(Boolean);
72
+ if (sources.length !== 1) throw new Error(`@mastra/livekit: MastraLLM requires exactly one reply source — \`remote\`, \`agent\`, or \`generate\` — but got ${sources.length === 0 ? "none" : sources.join(" + ")}.`);
73
+ this.#memory = options.memory ?? false;
74
+ this.#requestContext = toRequestContext(options.requestContext);
75
+ if (options.remote) {
76
+ this.#model = options.remote.agentId;
77
+ this.#remoteOptions = {
78
+ ...options.remote,
79
+ toolFeedback: options.toolFeedback,
80
+ onToolCall: options.onToolCall,
81
+ onTurnComplete: options.onTurnComplete
82
+ };
83
+ } else if (options.agent) {
84
+ this.#model = options.agent.id ?? options.agent.name;
85
+ this.#staticGenerator = require_remote.createAgentReplyGenerator({
86
+ agent: options.agent,
87
+ toolFeedback: options.toolFeedback,
88
+ onToolCall: options.onToolCall,
89
+ onTurnComplete: options.onTurnComplete
90
+ });
91
+ } else {
92
+ this.#model = "mastra-generator";
93
+ this.#staticGenerator = options.generate;
94
+ }
95
+ }
96
+ label() {
97
+ return "mastra.MastraLLM";
98
+ }
99
+ get model() {
100
+ return this.#model;
101
+ }
102
+ get provider() {
103
+ return "mastra";
104
+ }
105
+ /**
106
+ * Resolves the reply generator for a turn. Non-remote sources are built once; the remote transport
107
+ * is built per turn so its connect + first-token timeout can come from the session's
108
+ * `connOptions.timeoutMs`, with base-class retries owning retry (transport `retries: 0`).
109
+ */
110
+ resolveGenerator(connOptions) {
111
+ if (this.#staticGenerator) return this.#staticGenerator;
112
+ const remote = this.#remoteOptions;
113
+ return require_remote.createRemoteAgentReplyGenerator({
114
+ ...remote,
115
+ retries: 0,
116
+ timeoutMs: remote.timeoutMs ?? connOptions.timeoutMs
117
+ });
118
+ }
119
+ chat({ chatCtx, toolCtx, connOptions = _livekit_agents.DEFAULT_API_CONNECT_OPTIONS }) {
120
+ if (!this.#warnedToolCtx && toolCtx) {
121
+ const ignored = livekitToolNames(toolCtx);
122
+ if (ignored.length > 0) {
123
+ this.#warnedToolCtx = true;
124
+ console.warn(`@mastra/livekit: MastraLLM ignores LiveKit-side tools (${ignored.join(", ")}). Tools are defined and executed server-side on the Mastra agent — move them there.`);
125
+ }
126
+ }
127
+ return new MastraLLMStream(this, {
128
+ chatCtx,
129
+ toolCtx,
130
+ connOptions,
131
+ generator: this.resolveGenerator(connOptions),
132
+ onTurnMetrics: this.#onTurnMetrics,
133
+ memory: this.#memory,
134
+ requestContext: this.#requestContext
135
+ });
136
+ }
137
+ /** No-op in v1: nothing in the LiveKit session/worker ever calls `prewarm()` automatically. */
138
+ prewarm() {}
139
+ };
140
+ /**
141
+ * The `llm.LLMStream` `MastraLLM` returns per turn. `run()` extracts the turn's messages, drives
142
+ * the reply generator, and pushes assistant `ChatChunk`s into `this.queue` (NOT `this.output` — the
143
+ * base class drains queue → output and computes TTFT / duration / usage). Barge-in aborts via
144
+ * `this.abortController` and `run()` returns silently.
145
+ */
146
+ var MastraLLMStream = class extends _livekit_agents.llm.LLMStream {
147
+ #generator;
148
+ #onTurnMetrics;
149
+ #memory;
150
+ #requestContext;
151
+ constructor(mastraLLM, options) {
152
+ super(mastraLLM, {
153
+ chatCtx: options.chatCtx,
154
+ toolCtx: options.toolCtx,
155
+ connOptions: options.connOptions
156
+ });
157
+ this.#generator = options.generator;
158
+ this.#onTurnMetrics = options.onTurnMetrics;
159
+ this.#memory = options.memory;
160
+ this.#requestContext = options.requestContext;
161
+ }
162
+ async run() {
163
+ const messages = this.#memory === false ? require_remote.chatContextToMessages(this.chatCtx) : require_remote.extractNewTurnMessages(this.chatCtx);
164
+ if (messages.length === 0) return;
165
+ let usage;
166
+ const turnCtx = {
167
+ messages,
168
+ chatCtx: this.chatCtx,
169
+ memory: this.#memory,
170
+ requestContext: this.#requestContext,
171
+ onUsage: (turnUsage) => {
172
+ usage = turnUsage;
173
+ }
174
+ };
175
+ const { reply, identity } = await require_remote.instrumentVoiceReply(this.#generator, turnCtx, this.#onTurnMetrics);
176
+ if (!reply) return;
177
+ if (this.abortController.signal.aborted) {
178
+ await reply.cancel().catch(() => {});
179
+ return;
180
+ }
181
+ const id = identity.attemptId;
182
+ const reader = reply.getReader();
183
+ const onAbort = () => void reader.cancel().catch(() => {});
184
+ this.abortController.signal.addEventListener("abort", onAbort, { once: true });
185
+ try {
186
+ for (;;) {
187
+ const { done, value } = await reader.read();
188
+ if (done) break;
189
+ if (this.abortController.signal.aborted) break;
190
+ if (value) this.queue.put({
191
+ id,
192
+ delta: {
193
+ role: "assistant",
194
+ ...typeof value === "string" ? { content: value } : {},
195
+ extra: {
196
+ [require_turn_metrics.VOICE_TURN_METADATA]: identity,
197
+ [require_turn_metrics.VOICE_FLUSH_METADATA]: typeof value !== "string"
198
+ }
199
+ }
200
+ });
201
+ }
202
+ } catch (error) {
203
+ if (this.abortController.signal.aborted) return;
204
+ throw error;
205
+ } finally {
206
+ this.abortController.signal.removeEventListener("abort", onAbort);
207
+ }
208
+ if (this.abortController.signal.aborted) return;
209
+ if (usage) this.queue.put({
210
+ id,
211
+ usage: mapUsageToLiveKit(usage)
212
+ });
213
+ }
214
+ };
215
+ //#endregion
216
+ exports.MastraLLM = MastraLLM;
217
+ exports.VOICE_TEXT_FLUSH = require_turn_metrics.VOICE_TEXT_FLUSH;
218
+ exports.createRemoteAgentReplyGenerator = require_remote.createRemoteAgentReplyGenerator;
219
+ exports.mastraLLMNode = require_remote.mastraLLMNode;
220
+ exports.observeVoiceSession = require_remote.observeVoiceSession;
221
+
222
+ //# sourceMappingURL=plugin-entry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-entry.cjs","names":["RequestContext","llm","#model","#onTurnMetrics","#memory","#requestContext","#staticGenerator","#remoteOptions","createAgentReplyGenerator","createRemoteAgentReplyGenerator","DEFAULT_API_CONNECT_OPTIONS","#warnedToolCtx","#generator","chatContextToMessages","extractNewTurnMessages","instrumentVoiceReply","VOICE_TURN_METADATA","VOICE_FLUSH_METADATA"],"sources":["../src/llm-plugin.ts"],"sourcesContent":["import { DEFAULT_API_CONNECT_OPTIONS, llm } from '@livekit/agents';\nimport type { APIConnectOptions } from '@livekit/agents';\nimport type { Agent as MastraAgent } from '@mastra/core/agent';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { createAgentReplyGenerator, instrumentVoiceReply } from './bridge';\nimport type {\n MastraVoiceAgentMemory,\n VoiceReplyGenerator,\n VoiceToolCall,\n VoiceTurnCompleteHook,\n VoiceTurnContext,\n VoiceTurnUsage,\n} from './bridge';\nimport { chatContextToMessages, extractNewTurnMessages } from './messages';\nimport { createRemoteAgentReplyGenerator } from './remote';\nimport type { RemoteMastraAgentOptions } from './remote';\nimport type { VoiceTurnMetricsHook } from './turn-metrics';\nimport { VOICE_FLUSH_METADATA, VOICE_TURN_METADATA } from './turn-metrics';\n\nexport type { RemoteMastraAgentOptions } from './remote';\n\n/**\n * Options for {@link MastraLLM}. Provide **exactly one** reply source — `remote` (the headline: a\n * Mastra app on a remote server), `agent` (an in-process Mastra agent), or `generate` (a custom\n * {@link VoiceReplyGenerator}). The `toolFeedback` / `onToolCall` / `onTurnComplete` hooks apply to\n * the `remote` and `agent` sources; a `generate` source owns its own hooks.\n */\nexport interface MastraLLMOptions {\n /** Per-attempt metrics; use observeVoiceSession for playback metrics. */\n onTurnMetrics?: VoiceTurnMetricsHook;\n /** Remote Mastra server. Provide exactly one of `remote`, `agent`, `generate`. */\n remote?: RemoteMastraAgentOptions;\n /** In-process Mastra agent (reuses `createAgentReplyGenerator`). */\n agent?: MastraAgent;\n /** Custom reply source (escape hatch; owns its own tool-feedback / turn-complete behavior). */\n generate?: VoiceReplyGenerator;\n\n /**\n * Conversation persistence, resolved by the customer per call (from SIP/caller identity). When set,\n * only messages new since the agent last spoke are sent each turn and Mastra Memory supplies\n * history. When omitted/false, the full LiveKit chat context is sent every turn.\n *\n * NOTE: incompatible with the session's `preemptiveGeneration` option — a speculative turn\n * persists a partial user message and a partial reply before being discarded. Leave preemptive\n * generation off when using `memory`, or set `memory.options.readOnly` and persist committed\n * turns yourself from the session's conversation-item events.\n *\n * The plugin cannot detect the combination at runtime, so this stays a documented constraint\n * rather than a warning: the LLM interface never receives the session (so the option can't be\n * read), a preemptive `chat()` is shape-identical to a real turn (LiveKit drives the same\n * `generateReply` with a draft transcript and no marker), and the observable signature — a\n * cancelled stream followed by a `chat()` whose trailing user message changed — is exactly what\n * an ordinary barge-in correction looks like, so a heuristic would warn on every barge-in.\n */\n memory?: MastraVoiceAgentMemory | false;\n /** Request context forwarded to generation (tenant, dialed number, ...). */\n requestContext?: RequestContext | Record<string, unknown>;\n\n /** Speak a short filler while a (server-side) tool runs. Applies to the `remote`/`agent` sources. */\n toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void;\n /** Notified as each tool-call chunk arrives, mid-stream. Applies to the `remote`/`agent` sources. */\n onToolCall?: (toolCall: VoiceToolCall) => void;\n /** Fired off the audio path after each reply finishes. Applies to the `remote`/`agent` sources. */\n onTurnComplete?: VoiceTurnCompleteHook;\n}\n\nfunction toRequestContext(value: RequestContext | Record<string, unknown> | undefined): RequestContext | undefined {\n if (!value) return undefined;\n if (value instanceof RequestContext) return value;\n return new RequestContext<unknown>(Object.entries(value));\n}\n\nfunction mapUsageToLiveKit(usage: VoiceTurnUsage): llm.CompletionUsage {\n return {\n completionTokens: usage.completionTokens,\n promptTokens: usage.promptTokens,\n promptCachedTokens: usage.promptCachedTokens,\n totalTokens: usage.totalTokens,\n };\n}\n\n/**\n * Tool names carried by a `toolCtx`, across @livekit/agents versions: 1.5+ always passes a\n * `ToolContext` class instance (tools behind getters, `Object.keys` sees only private fields),\n * while 1.4 and the object shorthand pass a plain name→tool map.\n */\nfunction livekitToolNames(toolCtx: object): string[] {\n const instance = toolCtx as { flatten?: unknown };\n if (typeof instance.flatten === 'function') {\n return (instance.flatten as () => object[])().map(tool => {\n const { id, name } = tool as { id?: string; name?: string };\n return id ?? name ?? 'unknown';\n });\n }\n return Object.keys(toolCtx);\n}\n\n/**\n * A standard LiveKit LLM plugin (`llm.LLM`) backed by a Mastra agent. Drop it into the `llm` slot of a\n * customer-owned `voice.AgentSession` and the Mastra app (agent loop, tools, memory, observability)\n * runs wherever it's deployed — most importantly on a **remote** Mastra server reached over HTTP.\n *\n * Tools are defined and executed **server-side** on the Mastra agent; LiveKit-side `toolCtx` is\n * ignored (with a one-time warning). Tool activity surfaces via `toolFeedback` (spoken) and\n * `onToolCall` / `onTurnComplete` (programmatic). `voice.Agent` instructions do **not** reach the\n * Mastra agent — put instructions on the Mastra agent instead.\n *\n * @example\n * ```ts\n * const session = new voice.AgentSession({\n * stt: 'deepgram/nova-3',\n * tts: 'cartesia/sonic-3',\n * llm: new MastraLLM({\n * remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'callCenter' },\n * memory: { thread: callId, resource: callerId },\n * }),\n * });\n * ```\n */\nexport class MastraLLM extends llm.LLM {\n readonly #model: string;\n readonly #onTurnMetrics?: VoiceTurnMetricsHook;\n readonly #memory: MastraVoiceAgentMemory | false;\n readonly #requestContext?: RequestContext;\n /** Non-remote sources are built once; remote is built per turn so it can pick up `connOptions.timeoutMs`. */\n readonly #staticGenerator?: VoiceReplyGenerator;\n readonly #remoteOptions?: RemoteMastraAgentOptions & {\n toolFeedback?: MastraLLMOptions['toolFeedback'];\n onToolCall?: MastraLLMOptions['onToolCall'];\n onTurnComplete?: VoiceTurnCompleteHook;\n };\n #warnedToolCtx = false;\n\n constructor(options: MastraLLMOptions) {\n super();\n this.#onTurnMetrics = options.onTurnMetrics;\n const sources = [\n options.remote ? 'remote' : undefined,\n options.agent ? 'agent' : undefined,\n options.generate ? 'generate' : undefined,\n ].filter(Boolean) as string[];\n if (sources.length !== 1) {\n throw new Error(\n `@mastra/livekit: MastraLLM requires exactly one reply source — \\`remote\\`, \\`agent\\`, or \\`generate\\` — ` +\n `but got ${sources.length === 0 ? 'none' : sources.join(' + ')}.`,\n );\n }\n\n this.#memory = options.memory ?? false;\n this.#requestContext = toRequestContext(options.requestContext);\n\n if (options.remote) {\n this.#model = options.remote.agentId;\n this.#remoteOptions = {\n ...options.remote,\n toolFeedback: options.toolFeedback,\n onToolCall: options.onToolCall,\n onTurnComplete: options.onTurnComplete,\n };\n } else if (options.agent) {\n this.#model = options.agent.id ?? options.agent.name;\n this.#staticGenerator = createAgentReplyGenerator({\n agent: options.agent,\n toolFeedback: options.toolFeedback,\n onToolCall: options.onToolCall,\n onTurnComplete: options.onTurnComplete,\n });\n } else {\n this.#model = 'mastra-generator';\n this.#staticGenerator = options.generate;\n }\n }\n\n label(): string {\n return 'mastra.MastraLLM';\n }\n\n override get model(): string {\n return this.#model;\n }\n\n override get provider(): string {\n return 'mastra';\n }\n\n /**\n * Resolves the reply generator for a turn. Non-remote sources are built once; the remote transport\n * is built per turn so its connect + first-token timeout can come from the session's\n * `connOptions.timeoutMs`, with base-class retries owning retry (transport `retries: 0`).\n */\n private resolveGenerator(connOptions: APIConnectOptions): VoiceReplyGenerator {\n if (this.#staticGenerator) return this.#staticGenerator;\n const remote = this.#remoteOptions!;\n return createRemoteAgentReplyGenerator({\n ...remote,\n retries: 0,\n timeoutMs: remote.timeoutMs ?? connOptions.timeoutMs,\n });\n }\n\n override chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n }): llm.LLMStream {\n // Tools run server-side; warn once if the customer wired LiveKit-side tools.\n if (!this.#warnedToolCtx && toolCtx) {\n const ignored = livekitToolNames(toolCtx);\n if (ignored.length > 0) {\n this.#warnedToolCtx = true;\n console.warn(\n `@mastra/livekit: MastraLLM ignores LiveKit-side tools (${ignored.join(', ')}). ` +\n `Tools are defined and executed server-side on the Mastra agent — move them there.`,\n );\n }\n }\n return new MastraLLMStream(this, {\n chatCtx,\n toolCtx,\n connOptions,\n generator: this.resolveGenerator(connOptions),\n onTurnMetrics: this.#onTurnMetrics,\n memory: this.#memory,\n requestContext: this.#requestContext,\n });\n }\n\n /** No-op in v1: nothing in the LiveKit session/worker ever calls `prewarm()` automatically. */\n override prewarm(): void {}\n}\n\ninterface MastraLLMStreamOptions {\n onTurnMetrics?: VoiceTurnMetricsHook;\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n generator: VoiceReplyGenerator;\n memory: MastraVoiceAgentMemory | false;\n requestContext?: RequestContext;\n}\n\n/**\n * The `llm.LLMStream` `MastraLLM` returns per turn. `run()` extracts the turn's messages, drives\n * the reply generator, and pushes assistant `ChatChunk`s into `this.queue` (NOT `this.output` — the\n * base class drains queue → output and computes TTFT / duration / usage). Barge-in aborts via\n * `this.abortController` and `run()` returns silently.\n */\nclass MastraLLMStream extends llm.LLMStream {\n readonly #generator: VoiceReplyGenerator;\n readonly #onTurnMetrics?: VoiceTurnMetricsHook;\n readonly #memory: MastraVoiceAgentMemory | false;\n readonly #requestContext?: RequestContext;\n\n constructor(mastraLLM: MastraLLM, options: MastraLLMStreamOptions) {\n super(mastraLLM, { chatCtx: options.chatCtx, toolCtx: options.toolCtx, connOptions: options.connOptions });\n this.#generator = options.generator;\n this.#onTurnMetrics = options.onTurnMetrics;\n this.#memory = options.memory;\n this.#requestContext = options.requestContext;\n }\n\n protected async run(): Promise<void> {\n const messages =\n this.#memory === false ? chatContextToMessages(this.chatCtx) : extractNewTurnMessages(this.chatCtx);\n // No new input to answer → close without a request (equivalent to the wrapper returning null).\n if (messages.length === 0) return;\n\n let usage: VoiceTurnUsage | undefined;\n const turnCtx: VoiceTurnContext = {\n messages,\n chatCtx: this.chatCtx,\n memory: this.#memory,\n requestContext: this.#requestContext,\n onUsage: turnUsage => {\n usage = turnUsage;\n },\n };\n\n const { reply, identity } = await instrumentVoiceReply(this.#generator, turnCtx, this.#onTurnMetrics);\n if (!reply) return;\n if (this.abortController.signal.aborted) {\n await reply.cancel().catch(() => {});\n return;\n }\n\n // A single provider response id ties all of this turn's chunks together for the base class metrics.\n const id = identity.attemptId;\n const reader = reply.getReader();\n const onAbort = () => void reader.cancel().catch(() => {});\n this.abortController.signal.addEventListener('abort', onAbort, { once: true });\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (this.abortController.signal.aborted) break;\n if (value)\n this.queue.put({\n id,\n delta: {\n role: 'assistant',\n ...(typeof value === 'string' ? { content: value } : {}),\n extra: { [VOICE_TURN_METADATA]: identity, [VOICE_FLUSH_METADATA]: typeof value !== 'string' },\n },\n });\n }\n } catch (error) {\n // Barge-in tears down the reply stream; that surfaces as a read rejection but is not a failure.\n if (this.abortController.signal.aborted) return;\n throw error;\n } finally {\n this.abortController.signal.removeEventListener('abort', onAbort);\n }\n // Return silently on barge-in — throwing would feed the base class's error/retry machinery.\n if (this.abortController.signal.aborted) return;\n // Final usage-only chunk → the base class reads usage from the last chunk that carries it.\n if (usage) this.queue.put({ id, usage: mapUsageToLiveKit(usage) });\n }\n}\n\nexport { MastraLLMStream };\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { MastraLLM } from './llm-plugin.js';
2
+ export type { MastraLLMOptions } from './llm-plugin.js';
3
+ export { createRemoteAgentReplyGenerator } from './remote.js';
4
+ export type { RemoteMastraAgentOptions, RemoteAgentReplyGeneratorOptions } from './remote.js';
5
+ export type { MastraVoiceAgentMemory, VoiceReplyGenerator, VoiceToolCall, VoiceTurnCompleteContext, VoiceTurnCompleteHook, VoiceTurnContext, VoiceTurnResult, VoiceTurnUsage, } from './bridge.js';
6
+ export { observeVoiceSession, mastraLLMNode } from './speech.js';
7
+ export type { ObserveVoiceSessionOptions } from './speech.js';
8
+ export { VOICE_TEXT_FLUSH } from './turn-metrics.js';
9
+ export type { VoiceReplyChunk, VoiceOutcome, VoiceTurnIdentity, VoiceToolTiming, VoiceGenerationMetrics, VoiceSpeechMetrics, VoiceTurnMetrics, VoiceTurnMetricsHook, VoiceSpeechResult, VoiceSpeechCompleteHook, } from './turn-metrics.js';
10
+ //# sourceMappingURL=plugin-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-entry.d.ts","sourceRoot":"","sources":["../src/plugin-entry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,+BAA+B,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,UAAU,CAAC;AAC3F,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9D,YAAY,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EACV,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,217 @@
1
+ import { n as VOICE_TEXT_FLUSH, r as VOICE_TURN_METADATA, t as VOICE_FLUSH_METADATA } from "./turn-metrics-CqqzdLDe.js";
2
+ import { a as createAgentReplyGenerator, c as chatContextToMessages, l as extractNewTurnMessages, n as mastraLLMNode, r as observeVoiceSession, s as instrumentVoiceReply, t as createRemoteAgentReplyGenerator } from "./remote-CIMCGicy.js";
3
+ import { DEFAULT_API_CONNECT_OPTIONS, llm } from "@livekit/agents";
4
+ import { RequestContext } from "@mastra/core/request-context";
5
+ //#region src/llm-plugin.ts
6
+ function toRequestContext(value) {
7
+ if (!value) return void 0;
8
+ if (value instanceof RequestContext) return value;
9
+ return new RequestContext(Object.entries(value));
10
+ }
11
+ function mapUsageToLiveKit(usage) {
12
+ return {
13
+ completionTokens: usage.completionTokens,
14
+ promptTokens: usage.promptTokens,
15
+ promptCachedTokens: usage.promptCachedTokens,
16
+ totalTokens: usage.totalTokens
17
+ };
18
+ }
19
+ /**
20
+ * Tool names carried by a `toolCtx`, across @livekit/agents versions: 1.5+ always passes a
21
+ * `ToolContext` class instance (tools behind getters, `Object.keys` sees only private fields),
22
+ * while 1.4 and the object shorthand pass a plain name→tool map.
23
+ */
24
+ function livekitToolNames(toolCtx) {
25
+ const instance = toolCtx;
26
+ if (typeof instance.flatten === "function") return instance.flatten().map((tool) => {
27
+ const { id, name } = tool;
28
+ return id ?? name ?? "unknown";
29
+ });
30
+ return Object.keys(toolCtx);
31
+ }
32
+ /**
33
+ * A standard LiveKit LLM plugin (`llm.LLM`) backed by a Mastra agent. Drop it into the `llm` slot of a
34
+ * customer-owned `voice.AgentSession` and the Mastra app (agent loop, tools, memory, observability)
35
+ * runs wherever it's deployed — most importantly on a **remote** Mastra server reached over HTTP.
36
+ *
37
+ * Tools are defined and executed **server-side** on the Mastra agent; LiveKit-side `toolCtx` is
38
+ * ignored (with a one-time warning). Tool activity surfaces via `toolFeedback` (spoken) and
39
+ * `onToolCall` / `onTurnComplete` (programmatic). `voice.Agent` instructions do **not** reach the
40
+ * Mastra agent — put instructions on the Mastra agent instead.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const session = new voice.AgentSession({
45
+ * stt: 'deepgram/nova-3',
46
+ * tts: 'cartesia/sonic-3',
47
+ * llm: new MastraLLM({
48
+ * remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'callCenter' },
49
+ * memory: { thread: callId, resource: callerId },
50
+ * }),
51
+ * });
52
+ * ```
53
+ */
54
+ var MastraLLM = class extends llm.LLM {
55
+ #model;
56
+ #onTurnMetrics;
57
+ #memory;
58
+ #requestContext;
59
+ /** Non-remote sources are built once; remote is built per turn so it can pick up `connOptions.timeoutMs`. */
60
+ #staticGenerator;
61
+ #remoteOptions;
62
+ #warnedToolCtx = false;
63
+ constructor(options) {
64
+ super();
65
+ this.#onTurnMetrics = options.onTurnMetrics;
66
+ const sources = [
67
+ options.remote ? "remote" : void 0,
68
+ options.agent ? "agent" : void 0,
69
+ options.generate ? "generate" : void 0
70
+ ].filter(Boolean);
71
+ if (sources.length !== 1) throw new Error(`@mastra/livekit: MastraLLM requires exactly one reply source — \`remote\`, \`agent\`, or \`generate\` — but got ${sources.length === 0 ? "none" : sources.join(" + ")}.`);
72
+ this.#memory = options.memory ?? false;
73
+ this.#requestContext = toRequestContext(options.requestContext);
74
+ if (options.remote) {
75
+ this.#model = options.remote.agentId;
76
+ this.#remoteOptions = {
77
+ ...options.remote,
78
+ toolFeedback: options.toolFeedback,
79
+ onToolCall: options.onToolCall,
80
+ onTurnComplete: options.onTurnComplete
81
+ };
82
+ } else if (options.agent) {
83
+ this.#model = options.agent.id ?? options.agent.name;
84
+ this.#staticGenerator = createAgentReplyGenerator({
85
+ agent: options.agent,
86
+ toolFeedback: options.toolFeedback,
87
+ onToolCall: options.onToolCall,
88
+ onTurnComplete: options.onTurnComplete
89
+ });
90
+ } else {
91
+ this.#model = "mastra-generator";
92
+ this.#staticGenerator = options.generate;
93
+ }
94
+ }
95
+ label() {
96
+ return "mastra.MastraLLM";
97
+ }
98
+ get model() {
99
+ return this.#model;
100
+ }
101
+ get provider() {
102
+ return "mastra";
103
+ }
104
+ /**
105
+ * Resolves the reply generator for a turn. Non-remote sources are built once; the remote transport
106
+ * is built per turn so its connect + first-token timeout can come from the session's
107
+ * `connOptions.timeoutMs`, with base-class retries owning retry (transport `retries: 0`).
108
+ */
109
+ resolveGenerator(connOptions) {
110
+ if (this.#staticGenerator) return this.#staticGenerator;
111
+ const remote = this.#remoteOptions;
112
+ return createRemoteAgentReplyGenerator({
113
+ ...remote,
114
+ retries: 0,
115
+ timeoutMs: remote.timeoutMs ?? connOptions.timeoutMs
116
+ });
117
+ }
118
+ chat({ chatCtx, toolCtx, connOptions = DEFAULT_API_CONNECT_OPTIONS }) {
119
+ if (!this.#warnedToolCtx && toolCtx) {
120
+ const ignored = livekitToolNames(toolCtx);
121
+ if (ignored.length > 0) {
122
+ this.#warnedToolCtx = true;
123
+ console.warn(`@mastra/livekit: MastraLLM ignores LiveKit-side tools (${ignored.join(", ")}). Tools are defined and executed server-side on the Mastra agent — move them there.`);
124
+ }
125
+ }
126
+ return new MastraLLMStream(this, {
127
+ chatCtx,
128
+ toolCtx,
129
+ connOptions,
130
+ generator: this.resolveGenerator(connOptions),
131
+ onTurnMetrics: this.#onTurnMetrics,
132
+ memory: this.#memory,
133
+ requestContext: this.#requestContext
134
+ });
135
+ }
136
+ /** No-op in v1: nothing in the LiveKit session/worker ever calls `prewarm()` automatically. */
137
+ prewarm() {}
138
+ };
139
+ /**
140
+ * The `llm.LLMStream` `MastraLLM` returns per turn. `run()` extracts the turn's messages, drives
141
+ * the reply generator, and pushes assistant `ChatChunk`s into `this.queue` (NOT `this.output` — the
142
+ * base class drains queue → output and computes TTFT / duration / usage). Barge-in aborts via
143
+ * `this.abortController` and `run()` returns silently.
144
+ */
145
+ var MastraLLMStream = class extends llm.LLMStream {
146
+ #generator;
147
+ #onTurnMetrics;
148
+ #memory;
149
+ #requestContext;
150
+ constructor(mastraLLM, options) {
151
+ super(mastraLLM, {
152
+ chatCtx: options.chatCtx,
153
+ toolCtx: options.toolCtx,
154
+ connOptions: options.connOptions
155
+ });
156
+ this.#generator = options.generator;
157
+ this.#onTurnMetrics = options.onTurnMetrics;
158
+ this.#memory = options.memory;
159
+ this.#requestContext = options.requestContext;
160
+ }
161
+ async run() {
162
+ const messages = this.#memory === false ? chatContextToMessages(this.chatCtx) : extractNewTurnMessages(this.chatCtx);
163
+ if (messages.length === 0) return;
164
+ let usage;
165
+ const turnCtx = {
166
+ messages,
167
+ chatCtx: this.chatCtx,
168
+ memory: this.#memory,
169
+ requestContext: this.#requestContext,
170
+ onUsage: (turnUsage) => {
171
+ usage = turnUsage;
172
+ }
173
+ };
174
+ const { reply, identity } = await instrumentVoiceReply(this.#generator, turnCtx, this.#onTurnMetrics);
175
+ if (!reply) return;
176
+ if (this.abortController.signal.aborted) {
177
+ await reply.cancel().catch(() => {});
178
+ return;
179
+ }
180
+ const id = identity.attemptId;
181
+ const reader = reply.getReader();
182
+ const onAbort = () => void reader.cancel().catch(() => {});
183
+ this.abortController.signal.addEventListener("abort", onAbort, { once: true });
184
+ try {
185
+ for (;;) {
186
+ const { done, value } = await reader.read();
187
+ if (done) break;
188
+ if (this.abortController.signal.aborted) break;
189
+ if (value) this.queue.put({
190
+ id,
191
+ delta: {
192
+ role: "assistant",
193
+ ...typeof value === "string" ? { content: value } : {},
194
+ extra: {
195
+ [VOICE_TURN_METADATA]: identity,
196
+ [VOICE_FLUSH_METADATA]: typeof value !== "string"
197
+ }
198
+ }
199
+ });
200
+ }
201
+ } catch (error) {
202
+ if (this.abortController.signal.aborted) return;
203
+ throw error;
204
+ } finally {
205
+ this.abortController.signal.removeEventListener("abort", onAbort);
206
+ }
207
+ if (this.abortController.signal.aborted) return;
208
+ if (usage) this.queue.put({
209
+ id,
210
+ usage: mapUsageToLiveKit(usage)
211
+ });
212
+ }
213
+ };
214
+ //#endregion
215
+ export { MastraLLM, VOICE_TEXT_FLUSH, createRemoteAgentReplyGenerator, mastraLLMNode, observeVoiceSession };
216
+
217
+ //# sourceMappingURL=plugin-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-entry.js","names":["#model","#onTurnMetrics","#memory","#requestContext","#staticGenerator","#remoteOptions","#warnedToolCtx","#generator"],"sources":["../src/llm-plugin.ts"],"sourcesContent":["import { DEFAULT_API_CONNECT_OPTIONS, llm } from '@livekit/agents';\nimport type { APIConnectOptions } from '@livekit/agents';\nimport type { Agent as MastraAgent } from '@mastra/core/agent';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { createAgentReplyGenerator, instrumentVoiceReply } from './bridge';\nimport type {\n MastraVoiceAgentMemory,\n VoiceReplyGenerator,\n VoiceToolCall,\n VoiceTurnCompleteHook,\n VoiceTurnContext,\n VoiceTurnUsage,\n} from './bridge';\nimport { chatContextToMessages, extractNewTurnMessages } from './messages';\nimport { createRemoteAgentReplyGenerator } from './remote';\nimport type { RemoteMastraAgentOptions } from './remote';\nimport type { VoiceTurnMetricsHook } from './turn-metrics';\nimport { VOICE_FLUSH_METADATA, VOICE_TURN_METADATA } from './turn-metrics';\n\nexport type { RemoteMastraAgentOptions } from './remote';\n\n/**\n * Options for {@link MastraLLM}. Provide **exactly one** reply source — `remote` (the headline: a\n * Mastra app on a remote server), `agent` (an in-process Mastra agent), or `generate` (a custom\n * {@link VoiceReplyGenerator}). The `toolFeedback` / `onToolCall` / `onTurnComplete` hooks apply to\n * the `remote` and `agent` sources; a `generate` source owns its own hooks.\n */\nexport interface MastraLLMOptions {\n /** Per-attempt metrics; use observeVoiceSession for playback metrics. */\n onTurnMetrics?: VoiceTurnMetricsHook;\n /** Remote Mastra server. Provide exactly one of `remote`, `agent`, `generate`. */\n remote?: RemoteMastraAgentOptions;\n /** In-process Mastra agent (reuses `createAgentReplyGenerator`). */\n agent?: MastraAgent;\n /** Custom reply source (escape hatch; owns its own tool-feedback / turn-complete behavior). */\n generate?: VoiceReplyGenerator;\n\n /**\n * Conversation persistence, resolved by the customer per call (from SIP/caller identity). When set,\n * only messages new since the agent last spoke are sent each turn and Mastra Memory supplies\n * history. When omitted/false, the full LiveKit chat context is sent every turn.\n *\n * NOTE: incompatible with the session's `preemptiveGeneration` option — a speculative turn\n * persists a partial user message and a partial reply before being discarded. Leave preemptive\n * generation off when using `memory`, or set `memory.options.readOnly` and persist committed\n * turns yourself from the session's conversation-item events.\n *\n * The plugin cannot detect the combination at runtime, so this stays a documented constraint\n * rather than a warning: the LLM interface never receives the session (so the option can't be\n * read), a preemptive `chat()` is shape-identical to a real turn (LiveKit drives the same\n * `generateReply` with a draft transcript and no marker), and the observable signature — a\n * cancelled stream followed by a `chat()` whose trailing user message changed — is exactly what\n * an ordinary barge-in correction looks like, so a heuristic would warn on every barge-in.\n */\n memory?: MastraVoiceAgentMemory | false;\n /** Request context forwarded to generation (tenant, dialed number, ...). */\n requestContext?: RequestContext | Record<string, unknown>;\n\n /** Speak a short filler while a (server-side) tool runs. Applies to the `remote`/`agent` sources. */\n toolFeedback?: (toolCall: VoiceToolCall) => string | undefined | void;\n /** Notified as each tool-call chunk arrives, mid-stream. Applies to the `remote`/`agent` sources. */\n onToolCall?: (toolCall: VoiceToolCall) => void;\n /** Fired off the audio path after each reply finishes. Applies to the `remote`/`agent` sources. */\n onTurnComplete?: VoiceTurnCompleteHook;\n}\n\nfunction toRequestContext(value: RequestContext | Record<string, unknown> | undefined): RequestContext | undefined {\n if (!value) return undefined;\n if (value instanceof RequestContext) return value;\n return new RequestContext<unknown>(Object.entries(value));\n}\n\nfunction mapUsageToLiveKit(usage: VoiceTurnUsage): llm.CompletionUsage {\n return {\n completionTokens: usage.completionTokens,\n promptTokens: usage.promptTokens,\n promptCachedTokens: usage.promptCachedTokens,\n totalTokens: usage.totalTokens,\n };\n}\n\n/**\n * Tool names carried by a `toolCtx`, across @livekit/agents versions: 1.5+ always passes a\n * `ToolContext` class instance (tools behind getters, `Object.keys` sees only private fields),\n * while 1.4 and the object shorthand pass a plain name→tool map.\n */\nfunction livekitToolNames(toolCtx: object): string[] {\n const instance = toolCtx as { flatten?: unknown };\n if (typeof instance.flatten === 'function') {\n return (instance.flatten as () => object[])().map(tool => {\n const { id, name } = tool as { id?: string; name?: string };\n return id ?? name ?? 'unknown';\n });\n }\n return Object.keys(toolCtx);\n}\n\n/**\n * A standard LiveKit LLM plugin (`llm.LLM`) backed by a Mastra agent. Drop it into the `llm` slot of a\n * customer-owned `voice.AgentSession` and the Mastra app (agent loop, tools, memory, observability)\n * runs wherever it's deployed — most importantly on a **remote** Mastra server reached over HTTP.\n *\n * Tools are defined and executed **server-side** on the Mastra agent; LiveKit-side `toolCtx` is\n * ignored (with a one-time warning). Tool activity surfaces via `toolFeedback` (spoken) and\n * `onToolCall` / `onTurnComplete` (programmatic). `voice.Agent` instructions do **not** reach the\n * Mastra agent — put instructions on the Mastra agent instead.\n *\n * @example\n * ```ts\n * const session = new voice.AgentSession({\n * stt: 'deepgram/nova-3',\n * tts: 'cartesia/sonic-3',\n * llm: new MastraLLM({\n * remote: { baseUrl: process.env.MASTRA_URL!, agentId: 'callCenter' },\n * memory: { thread: callId, resource: callerId },\n * }),\n * });\n * ```\n */\nexport class MastraLLM extends llm.LLM {\n readonly #model: string;\n readonly #onTurnMetrics?: VoiceTurnMetricsHook;\n readonly #memory: MastraVoiceAgentMemory | false;\n readonly #requestContext?: RequestContext;\n /** Non-remote sources are built once; remote is built per turn so it can pick up `connOptions.timeoutMs`. */\n readonly #staticGenerator?: VoiceReplyGenerator;\n readonly #remoteOptions?: RemoteMastraAgentOptions & {\n toolFeedback?: MastraLLMOptions['toolFeedback'];\n onToolCall?: MastraLLMOptions['onToolCall'];\n onTurnComplete?: VoiceTurnCompleteHook;\n };\n #warnedToolCtx = false;\n\n constructor(options: MastraLLMOptions) {\n super();\n this.#onTurnMetrics = options.onTurnMetrics;\n const sources = [\n options.remote ? 'remote' : undefined,\n options.agent ? 'agent' : undefined,\n options.generate ? 'generate' : undefined,\n ].filter(Boolean) as string[];\n if (sources.length !== 1) {\n throw new Error(\n `@mastra/livekit: MastraLLM requires exactly one reply source — \\`remote\\`, \\`agent\\`, or \\`generate\\` — ` +\n `but got ${sources.length === 0 ? 'none' : sources.join(' + ')}.`,\n );\n }\n\n this.#memory = options.memory ?? false;\n this.#requestContext = toRequestContext(options.requestContext);\n\n if (options.remote) {\n this.#model = options.remote.agentId;\n this.#remoteOptions = {\n ...options.remote,\n toolFeedback: options.toolFeedback,\n onToolCall: options.onToolCall,\n onTurnComplete: options.onTurnComplete,\n };\n } else if (options.agent) {\n this.#model = options.agent.id ?? options.agent.name;\n this.#staticGenerator = createAgentReplyGenerator({\n agent: options.agent,\n toolFeedback: options.toolFeedback,\n onToolCall: options.onToolCall,\n onTurnComplete: options.onTurnComplete,\n });\n } else {\n this.#model = 'mastra-generator';\n this.#staticGenerator = options.generate;\n }\n }\n\n label(): string {\n return 'mastra.MastraLLM';\n }\n\n override get model(): string {\n return this.#model;\n }\n\n override get provider(): string {\n return 'mastra';\n }\n\n /**\n * Resolves the reply generator for a turn. Non-remote sources are built once; the remote transport\n * is built per turn so its connect + first-token timeout can come from the session's\n * `connOptions.timeoutMs`, with base-class retries owning retry (transport `retries: 0`).\n */\n private resolveGenerator(connOptions: APIConnectOptions): VoiceReplyGenerator {\n if (this.#staticGenerator) return this.#staticGenerator;\n const remote = this.#remoteOptions!;\n return createRemoteAgentReplyGenerator({\n ...remote,\n retries: 0,\n timeoutMs: remote.timeoutMs ?? connOptions.timeoutMs,\n });\n }\n\n override chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n }): llm.LLMStream {\n // Tools run server-side; warn once if the customer wired LiveKit-side tools.\n if (!this.#warnedToolCtx && toolCtx) {\n const ignored = livekitToolNames(toolCtx);\n if (ignored.length > 0) {\n this.#warnedToolCtx = true;\n console.warn(\n `@mastra/livekit: MastraLLM ignores LiveKit-side tools (${ignored.join(', ')}). ` +\n `Tools are defined and executed server-side on the Mastra agent — move them there.`,\n );\n }\n }\n return new MastraLLMStream(this, {\n chatCtx,\n toolCtx,\n connOptions,\n generator: this.resolveGenerator(connOptions),\n onTurnMetrics: this.#onTurnMetrics,\n memory: this.#memory,\n requestContext: this.#requestContext,\n });\n }\n\n /** No-op in v1: nothing in the LiveKit session/worker ever calls `prewarm()` automatically. */\n override prewarm(): void {}\n}\n\ninterface MastraLLMStreamOptions {\n onTurnMetrics?: VoiceTurnMetricsHook;\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n generator: VoiceReplyGenerator;\n memory: MastraVoiceAgentMemory | false;\n requestContext?: RequestContext;\n}\n\n/**\n * The `llm.LLMStream` `MastraLLM` returns per turn. `run()` extracts the turn's messages, drives\n * the reply generator, and pushes assistant `ChatChunk`s into `this.queue` (NOT `this.output` — the\n * base class drains queue → output and computes TTFT / duration / usage). Barge-in aborts via\n * `this.abortController` and `run()` returns silently.\n */\nclass MastraLLMStream extends llm.LLMStream {\n readonly #generator: VoiceReplyGenerator;\n readonly #onTurnMetrics?: VoiceTurnMetricsHook;\n readonly #memory: MastraVoiceAgentMemory | false;\n readonly #requestContext?: RequestContext;\n\n constructor(mastraLLM: MastraLLM, options: MastraLLMStreamOptions) {\n super(mastraLLM, { chatCtx: options.chatCtx, toolCtx: options.toolCtx, connOptions: options.connOptions });\n this.#generator = options.generator;\n this.#onTurnMetrics = options.onTurnMetrics;\n this.#memory = options.memory;\n this.#requestContext = options.requestContext;\n }\n\n protected async run(): Promise<void> {\n const messages =\n this.#memory === false ? chatContextToMessages(this.chatCtx) : extractNewTurnMessages(this.chatCtx);\n // No new input to answer → close without a request (equivalent to the wrapper returning null).\n if (messages.length === 0) return;\n\n let usage: VoiceTurnUsage | undefined;\n const turnCtx: VoiceTurnContext = {\n messages,\n chatCtx: this.chatCtx,\n memory: this.#memory,\n requestContext: this.#requestContext,\n onUsage: turnUsage => {\n usage = turnUsage;\n },\n };\n\n const { reply, identity } = await instrumentVoiceReply(this.#generator, turnCtx, this.#onTurnMetrics);\n if (!reply) return;\n if (this.abortController.signal.aborted) {\n await reply.cancel().catch(() => {});\n return;\n }\n\n // A single provider response id ties all of this turn's chunks together for the base class metrics.\n const id = identity.attemptId;\n const reader = reply.getReader();\n const onAbort = () => void reader.cancel().catch(() => {});\n this.abortController.signal.addEventListener('abort', onAbort, { once: true });\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (this.abortController.signal.aborted) break;\n if (value)\n this.queue.put({\n id,\n delta: {\n role: 'assistant',\n ...(typeof value === 'string' ? { content: value } : {}),\n extra: { [VOICE_TURN_METADATA]: identity, [VOICE_FLUSH_METADATA]: typeof value !== 'string' },\n },\n });\n }\n } catch (error) {\n // Barge-in tears down the reply stream; that surfaces as a read rejection but is not a failure.\n if (this.abortController.signal.aborted) return;\n throw error;\n } finally {\n this.abortController.signal.removeEventListener('abort', onAbort);\n }\n // Return silently on barge-in — throwing would feed the base class's error/retry machinery.\n if (this.abortController.signal.aborted) return;\n // Final usage-only chunk → the base class reads usage from the last chunk that carries it.\n if (usage) this.queue.put({ id, usage: mapUsageToLiveKit(usage) });\n }\n}\n\nexport { MastraLLMStream };\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ApiRoute, ContextWithMastra } from '@mastra/core/server';
2
+ import type { SpanRecord } from '@mastra/core/storage';
3
+ import { z } from 'zod/v4';
4
+ import type { LiveKitRecording } from './recording-types.js';
5
+ export declare const recordingSchema: z.ZodObject<{
6
+ url: z.ZodURL;
7
+ expiresAt: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>;
9
+ export interface LiveKitRecordingResolverArgs {
10
+ traceId: string;
11
+ /** Taken from the stored voice call span, never from the browser request. */
12
+ roomName: string;
13
+ span: SpanRecord;
14
+ context: ContextWithMastra;
15
+ }
16
+ export interface LiveKitRecordingRouteOptions {
17
+ /** Studio discovers this route at its default path: /voice/livekit/recordings/:traceId. */
18
+ path?: string;
19
+ /** Defaults to true. Only disable for a local demo. */
20
+ requiresAuth?: boolean;
21
+ /** Required trace-specific access policy. Only true permits trace lookup and URL resolution. */
22
+ authorize: (args: {
23
+ traceId: string;
24
+ context: ContextWithMastra;
25
+ }) => boolean | Promise<boolean>;
26
+ /** Return a playable URL, or undefined if the recording is not available yet. */
27
+ resolveRecording: (args: LiveKitRecordingResolverArgs) => Promise<LiveKitRecording | undefined>;
28
+ }
29
+ /** Resolves a voice call trace to a recording without persisting signed URLs in traces. */
30
+ export declare function liveKitRecordingRoute(options: LiveKitRecordingRouteOptions): ApiRoute;
31
+ //# sourceMappingURL=recording-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recording-route.d.ts","sourceRoot":"","sources":["../src/recording-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAA4B,MAAM,mBAAmB,CAAC;AAGpF,eAAO,MAAM,eAAe;;;iBAGY,CAAC;AAEzC,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,4BAA4B;IAC3C,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gGAAgG;IAChG,SAAS,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,iBAAiB,CAAA;KAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjG,iFAAiF;IACjF,gBAAgB,EAAE,CAAC,IAAI,EAAE,4BAA4B,KAAK,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;CACjG;AAED,2FAA2F;AAC3F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,QAAQ,CA4CrF"}
@@ -0,0 +1,11 @@
1
+ /** A playback URL, normally signed with a short expiry by your storage provider. */
2
+ export interface LiveKitRecording {
3
+ url: string;
4
+ expiresAt?: string;
5
+ }
6
+ export type LiveKitRecordingResponse = ({
7
+ status: 'ready';
8
+ } & LiveKitRecording) | {
9
+ status: 'unavailable';
10
+ };
11
+ //# sourceMappingURL=recording-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recording-types.d.ts","sourceRoot":"","sources":["../src/recording-types.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,wBAAwB,GAAG,CAAC;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,gBAAgB,CAAC,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { RoomEgress } from 'livekit-server-sdk';
2
+ /**
3
+ * LiveKit auto-egress settings, accepted as a plain object or a RoomEgress instance.
4
+ * Use `room.audioOnly` and `room.fileOutputs` for a single mixed audio recording.
5
+ * Storage credentials belong here on the server, never in session metadata or tokens.
6
+ */
7
+ export type LiveKitRecordingOptions = NonNullable<ConstructorParameters<typeof RoomEgress>[0]>;
8
+ /** A recording-enabled session cannot reuse an existing LiveKit room. */
9
+ export declare class LiveKitRecordingRoomConflictError extends Error {
10
+ readonly roomName: string;
11
+ constructor(roomName: string);
12
+ }
13
+ /** Creates a fresh room with recording configured before any agent or caller joins. */
14
+ export declare function createRecordingRoom(options: {
15
+ roomName: string;
16
+ serverUrl: string;
17
+ apiKey: string;
18
+ apiSecret: string;
19
+ recording: LiveKitRecordingOptions;
20
+ }): Promise<import("livekit-server-sdk").Room>;
21
+ //# sourceMappingURL=recording.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../src/recording.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AAEnE;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,WAAW,CAAC,qBAAqB,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/F,yEAAyE;AACzE,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAM3B;CACF;AAED,uFAAuF;AACvF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,uBAAuB,CAAC;CACpC,8CAeA"}