@mastra/livekit 0.5.1 → 0.5.2

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 (29) hide show
  1. package/dist/bridge.d.ts.map +1 -1
  2. package/dist/client-entry.cjs +1 -2
  3. package/dist/client-entry.js +1 -2
  4. package/dist/index.cjs +7 -13
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.js +7 -13
  7. package/dist/index.js.map +1 -1
  8. package/dist/plugin-entry.cjs +3 -4
  9. package/dist/plugin-entry.js +3 -4
  10. package/dist/{remote-Dgn5fQLl.js → remote-DNmutTEU.js} +8 -13
  11. package/dist/remote-DNmutTEU.js.map +1 -0
  12. package/dist/{remote-z8wjPDtk.cjs → remote-DO33RaMO.cjs} +8 -13
  13. package/dist/remote-DO33RaMO.cjs.map +1 -0
  14. package/dist/speech.d.ts.map +1 -1
  15. package/dist/{turn-metrics-CtVHHdJj.cjs → turn-metrics-Dd8pYEbh.cjs} +2 -3
  16. package/dist/{turn-metrics-CtVHHdJj.cjs.map → turn-metrics-Dd8pYEbh.cjs.map} +1 -1
  17. package/dist/{turn-metrics-CqqzdLDe.js → turn-metrics-K8_ttSW7.js} +2 -3
  18. package/dist/{turn-metrics-CqqzdLDe.js.map → turn-metrics-K8_ttSW7.js.map} +1 -1
  19. package/dist/worker-entry.cjs +8 -13
  20. package/dist/worker-entry.cjs.map +1 -1
  21. package/dist/worker-entry.js +8 -13
  22. package/dist/worker-entry.js.map +1 -1
  23. package/dist/{workflow-generator-D67FvbFe.cjs → workflow-generator-RonPOBE2.cjs} +5 -8
  24. package/dist/{workflow-generator-D67FvbFe.cjs.map → workflow-generator-RonPOBE2.cjs.map} +1 -1
  25. package/dist/{workflow-generator-5nHqlZV7.js → workflow-generator-j3_TfX1B.js} +5 -8
  26. package/dist/{workflow-generator-5nHqlZV7.js.map → workflow-generator-j3_TfX1B.js.map} +1 -1
  27. package/package.json +13 -13
  28. package/dist/remote-Dgn5fQLl.js.map +0 -1
  29. package/dist/remote-z8wjPDtk.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,IAAI,WAAW,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAyC,MAAM,gBAAgB,CAAC;AAChG,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK5E,+GAA+G;AAC/G,eAAO,MAAM,2BAA2B,2DAA2D,CAAC;AAEpG;;;;GAIG;AACH,qBAAa,kBAAkB;IAG3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHvB,OAAO,CAAC,MAAM,CAAS;IACvB,YACmB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAC7B,GAAG,GAAE,MAAmB,EAGzB;IACD;;2GAEuG;IACvG,GAAG,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,SAAS,CAGhD;IACD,gGAAgG;IAChG,aAAa,CAAC,GAAG,GAAE,MAAmB,GAAG,IAAI,CAE5C;CACF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,GAAG,MAAM,CAAC,CAoBlG;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAAC;AAE9E,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CA2BvE;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;IACrD;;;;;;;;OAQG;IACH,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,mGAAmG;IACnG,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC;IACzB,gFAAgF;IAChF,MAAM,EAAE,sBAAsB,GAAG,KAAK,CAAC;IACvC,+CAA+C;IAC/C,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qFAAqF;IACrF,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,0EAA0E;IAC1E,WAAW,EAAE,OAAO,CAAC;IACrB,iFAAiF;IACjF,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,oGAAoG;AACpG,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE,8CAA8C;IAC9C,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5F;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,GAAG,EAAE,gBAAgB,KAClB,cAAc,CAAC,eAAe,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,CAAC;AAE9F,MAAM,WAAW,0BAA0B;IACzC,uFAAuF;IACvF,KAAK,EAAE,WAAW,CAAC;IACnB,qFAAqF;IACrF,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,qGAAqG;IACrG,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACtE,4GAA4G;IAC5G,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,4GAA4G;IAC5G,cAAc,CAAC,EAAE,qBAAqB,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CAiHlG;AAED,MAAM,WAAW,uBAAuB;IACtC,+FAA+F;IAC/F,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B;;;;OAIG;IACH,MAAM,CAAC,EAAE,sBAAsB,GAAG,KAAK,CAAC;IACxC,uDAAuD;IACvD,cAAc,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACtE;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,oFAAoF;IACpF,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,2GAA2G;IAC3G,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,EAAE,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;IACvC,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;IACzC,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;IACzC,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;CAC5D;AAiCD;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,KAAK,CAAC,KAAK;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,GAAG,KAAK,CAAC;IAChD,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsB;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAqB;IAE/C,YAAY,OAAO,EAAE,uBAAuB,EAwC3C;IAEc,OAAO,CACpB,OAAO,EAAE,GAAG,CAAC,WAAW,EACxB,QAAQ,EAAE,GAAG,CAAC,WAAW,EACzB,cAAc,EAAE,KAAK,CAAC,aAAa,GAClC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC,GAAG,IAAI,CAAC,CAmCxE;CACF;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAEzF;AAED,gGAAgG;AAChG,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,mBAAmB,EAC9B,GAAG,EAAE,gBAAgB,EACrB,aAAa,CAAC,EAAE,oBAAoB,GACnC,OAAO,CAAC;IAAE,KAAK,EAAE,cAAc,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CA8C7G"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _mastra_client_js = require("@mastra/client-js");
3
- //#region src/client-entry.ts
3
+ // src/client-entry.ts
4
4
  /** Requests a fresh playback URL using the client's authentication and fetch configuration. */
5
5
  function getLiveKitRecording(client, traceId, options) {
6
6
  return new _mastra_client_js.MastraClient({
@@ -11,7 +11,6 @@ function getLiveKitRecording(client, traceId, options) {
11
11
  signal: options?.signal
12
12
  });
13
13
  }
14
- //#endregion
15
14
  exports.getLiveKitRecording = getLiveKitRecording;
16
15
 
17
16
  //# sourceMappingURL=client-entry.cjs.map
@@ -1,5 +1,5 @@
1
1
  import { MastraClient } from "@mastra/client-js";
2
- //#region src/client-entry.ts
2
+ // src/client-entry.ts
3
3
  /** Requests a fresh playback URL using the client's authentication and fetch configuration. */
4
4
  function getLiveKitRecording(client, traceId, options) {
5
5
  return new MastraClient({
@@ -10,7 +10,6 @@ function getLiveKitRecording(client, traceId, options) {
10
10
  signal: options?.signal
11
11
  });
12
12
  }
13
- //#endregion
14
13
  export { getLiveKitRecording };
15
14
 
16
15
  //# sourceMappingURL=client-entry.js.map
package/dist/index.cjs CHANGED
@@ -1,11 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_workflow_generator = require("./workflow-generator-D67FvbFe.cjs");
2
+ const require_workflow_generator = require("./workflow-generator-RonPOBE2.cjs");
3
3
  let _livekit_protocol = require("@livekit/protocol");
4
4
  let livekit_server_sdk = require("livekit-server-sdk");
5
5
  let zod_v4 = require("zod/v4");
6
6
  let _mastra_core_tools = require("@mastra/core/tools");
7
7
  let zod = require("zod");
8
- //#region src/recording.ts
8
+ // src/recording.ts
9
9
  /** A recording-enabled session cannot reuse an existing LiveKit room. */
10
10
  var LiveKitRecordingRoomConflictError = class extends Error {
11
11
  roomName;
@@ -26,8 +26,7 @@ async function createRecordingRoom(options) {
26
26
  egress: new livekit_server_sdk.RoomEgress(recording)
27
27
  });
28
28
  }
29
- //#endregion
30
- //#region src/dispatch.ts
29
+ // src/dispatch.ts
31
30
  function toHttpUrl(url) {
32
31
  return url.replace(/^ws/, "http");
33
32
  }
@@ -51,8 +50,7 @@ async function dispatchVoiceSession(options) {
51
50
  });
52
51
  return new livekit_server_sdk.AgentDispatchClient(toHttpUrl(serverUrl), apiKey, apiSecret).createDispatch(options.roomName, options.agentName ?? "mastra-voice", { metadata });
53
52
  }
54
- //#endregion
55
- //#region src/routes.ts
53
+ // src/routes.ts
56
54
  function stringField(body, key) {
57
55
  const value = body[key];
58
56
  return typeof value === "string" && value.length > 0 ? value : void 0;
@@ -136,8 +134,7 @@ function liveKitConnectionRoute(options = {}) {
136
134
  handler
137
135
  };
138
136
  }
139
- //#endregion
140
- //#region src/recording-route.ts
137
+ // src/recording-route.ts
141
138
  const traceIdSchema = zod_v4.z.string().min(1).max(256);
142
139
  const recordingSchema = zod_v4.z.object({
143
140
  url: zod_v4.z.url({ protocol: /^https?$/ }),
@@ -187,8 +184,7 @@ function liveKitRecordingRoute(options) {
187
184
  }
188
185
  };
189
186
  }
190
- //#endregion
191
- //#region src/consent.ts
187
+ // src/consent.ts
192
188
  const DEFAULT_DESCRIPTION$1 = "Record the caller's consent decision for a data-processing item (for example, storing a summary of the call). Call this as soon as the caller answers a consent question: granted = true if they agree, false if they decline. Do not proceed with an activity that requires consent until you have recorded the caller granting it.";
193
189
  /**
194
190
  * Builds a Mastra tool that captures the caller's consent decisions at runtime — the companion to
@@ -235,8 +231,7 @@ function createConsentTool(options) {
235
231
  }
236
232
  });
237
233
  }
238
- //#endregion
239
- //#region src/end-call.ts
234
+ // src/end-call.ts
240
235
  const DEFAULT_DESCRIPTION = "End the phone call. Call this only after you have said goodbye and there is nothing left to do — for example the caller says goodbye, or the task is complete and you have wrapped up. Say your closing line first, then call this as your final action; the call hangs up once your words finish playing. Your closing line must be a final statement, never a question — if you are asking the caller anything (for example whether they need anything else), do NOT call this in that reply; wait for their answer. Do not call it if the caller still needs something.";
241
236
  /**
242
237
  * Builds a Mastra tool the agent calls to end the call itself (say goodbye → hang up). It is the
@@ -276,7 +271,6 @@ function createEndCallTool(options = {}) {
276
271
  }
277
272
  });
278
273
  }
279
- //#endregion
280
274
  exports.DEFAULT_LIVEKIT_AGENT_NAME = require_workflow_generator.DEFAULT_LIVEKIT_AGENT_NAME;
281
275
  exports.LiveKitRecordingRoomConflictError = LiveKitRecordingRoomConflictError;
282
276
  exports.createConsentTool = createConsentTool;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["RoomServiceClient","RoomEgress","serializeSessionMetadata","AgentDispatchClient","AccessToken","RoomConfiguration","RoomAgentDispatch","serializeSessionMetadata","z","DEFAULT_DESCRIPTION","z","createTool","createTool","z"],"sources":["../src/recording.ts","../src/dispatch.ts","../src/routes.ts","../src/recording-route.ts","../src/consent.ts","../src/end-call.ts"],"sourcesContent":["import { RoomEgress, RoomServiceClient } from 'livekit-server-sdk';\n\n/**\n * LiveKit auto-egress settings, accepted as a plain object or a RoomEgress instance.\n * Use `room.audioOnly` and `room.fileOutputs` for a single mixed audio recording.\n * Storage credentials belong here on the server, never in session metadata or tokens.\n */\nexport type LiveKitRecordingOptions = NonNullable<ConstructorParameters<typeof RoomEgress>[0]>;\n\n/** A recording-enabled session cannot reuse an existing LiveKit room. */\nexport class LiveKitRecordingRoomConflictError extends Error {\n readonly roomName: string;\n\n constructor(roomName: string) {\n super(\n `@mastra/livekit: recording requires a new room, but room \"${roomName}\" already exists. Use a fresh room name.`,\n );\n this.name = 'LiveKitRecordingRoomConflictError';\n this.roomName = roomName;\n }\n}\n\n/** Creates a fresh room with recording configured before any agent or caller joins. */\nexport async function createRecordingRoom(options: {\n roomName: string;\n serverUrl: string;\n apiKey: string;\n apiSecret: string;\n recording: LiveKitRecordingOptions;\n}) {\n const { recording, roomName } = options;\n if (!recording.room && !recording.participant && !recording.tracks) {\n throw new Error('@mastra/livekit: recording must configure at least one of room, participant, or tracks.');\n }\n\n const client = new RoomServiceClient(options.serverUrl.replace(/^ws/, 'http'), options.apiKey, options.apiSecret);\n // CreateRoom reuses existing rooms without applying room-composite auto-egress.\n // This preflight is not atomic: callers must use a fresh name and serialize room creation.\n const rooms = await client.listRooms([roomName]);\n if (rooms.length > 0) {\n throw new LiveKitRecordingRoomConflictError(roomName);\n }\n\n return client.createRoom({ name: roomName, egress: new RoomEgress(recording) });\n}\n","import { AgentDispatchClient } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { createRecordingRoom } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\nexport interface DispatchVoiceSessionOptions {\n /**\n * Room to dispatch the agent into (created on demand). With recording enabled, use a unique\n * name for each call. An existing room throws LiveKitRecordingRoomConflictError.\n */\n roomName: string;\n /** Must match the worker's `agentName`. Defaults to `'mastra-voice'`. */\n agentName?: string;\n metadata?: LiveKitSessionMetadata;\n /** Create a fresh room with automatic recording before dispatch. Omit to keep dispatch-only behavior. */\n recording?: LiveKitRecordingOptions;\n /** Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n}\n\nfunction toHttpUrl(url: string): string {\n return url.replace(/^ws/, 'http');\n}\n\n/**\n * Programmatically dispatches a Mastra voice agent into a LiveKit room — for\n * server-initiated sessions such as outbound calls or joining an existing room.\n */\nexport async function dispatchVoiceSession(options: DispatchVoiceSessionOptions) {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl) {\n throw new Error('@mastra/livekit: set LIVEKIT_URL or pass serverUrl to dispatchVoiceSession.');\n }\n if (!apiKey || !apiSecret) {\n throw new Error(\n '@mastra/livekit: set LIVEKIT_API_KEY and LIVEKIT_API_SECRET or pass apiKey/apiSecret to dispatchVoiceSession.',\n );\n }\n const metadata = serializeSessionMetadata(options.metadata ?? {});\n if (options.recording !== undefined) {\n await createRecordingRoom({\n roomName: options.roomName,\n serverUrl,\n apiKey,\n apiSecret,\n recording: options.recording,\n });\n }\n const client = new AgentDispatchClient(toHttpUrl(serverUrl), apiKey, apiSecret);\n return client.createDispatch(options.roomName, options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME, {\n metadata,\n });\n}\n","import { RoomAgentDispatch, RoomConfiguration } from '@livekit/protocol';\nimport type { ContextWithMastra, ApiRoute } from '@mastra/core/server';\nimport { AccessToken } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { dispatchVoiceSession } from './dispatch';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { LiveKitRecordingRoomConflictError } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\n/** Response body of the connection-details route. Matches LiveKit's frontend starter contract. */\nexport interface LiveKitConnectionDetails {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n}\n\nexport interface ConnectionRequestArgs {\n body: Record<string, unknown>;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitConnectionRouteOptions {\n /**\n * Route path. Defaults to `/voice/livekit/connection-details`. Mastra reserves the\n * `/api` prefix for built-in routes, so custom paths must not start with `/api`.\n */\n path?: string;\n /** LiveKit server URL (`wss://...`). Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n /** LiveKit agent name for explicit dispatch. Must match the worker's `agentName`. */\n agentName?: string;\n /** Token time-to-live. Defaults to `'15m'`. */\n ttl?: string | number;\n /** Defaults to `true` (Mastra custom routes require auth unless opted out). */\n requiresAuth?: boolean;\n /**\n * Room name or a function that derives one from the request. Defaults to a generated name.\n * With recording enabled, use a unique name for each call. An existing room returns HTTP 409\n * before agent dispatch or token issuance.\n */\n roomName?: string | ((args: ConnectionRequestArgs) => string);\n participantIdentity?: string | ((args: ConnectionRequestArgs) => string);\n /**\n * Session metadata delivered to the worker via agent dispatch. Defaults to passing\n * through `agentId`, `threadId`, and `resourceId` from the request body.\n */\n metadata?: (args: ConnectionRequestArgs) => LiveKitSessionMetadata | Promise<LiveKitSessionMetadata>;\n /**\n * Create a fresh room with automatic recording and dispatch the agent before returning a token.\n * The server-side callback receives the resolved room name; return undefined to skip recording.\n * Recording settings and storage credentials are never included in the participant token.\n */\n recording?:\n | LiveKitRecordingOptions\n | ((\n args: ConnectionRequestArgs & { roomName: string },\n ) => LiveKitRecordingOptions | undefined | Promise<LiveKitRecordingOptions | undefined>);\n}\n\nfunction stringField(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * An ApiRoute for `server.apiRoutes` that mints a LiveKit access token with the Mastra\n * voice agent dispatched into the room. Frontends call it to join a voice session:\n *\n * ```ts\n * export const mastra = new Mastra({\n * agents: { support },\n * server: { apiRoutes: [liveKitConnectionRoute({ agentName: 'mastra-voice' })] },\n * });\n * ```\n */\nexport function liveKitConnectionRoute(options: LiveKitConnectionRouteOptions = {}): ApiRoute {\n const handler = async (c: ContextWithMastra) => {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl || !apiKey || !apiSecret) {\n return c.json(\n {\n error:\n 'LiveKit is not configured. Set LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET ' +\n '(or pass serverUrl/apiKey/apiSecret to liveKitConnectionRoute).',\n },\n 500,\n );\n }\n\n const body: Record<string, unknown> = await c.req\n .json()\n .then((parsed: unknown) =>\n parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {},\n )\n .catch(() => ({}));\n const args: ConnectionRequestArgs = { body, context: c };\n\n const metadata: LiveKitSessionMetadata = options.metadata\n ? await options.metadata(args)\n : {\n agentId: stringField(body, 'agentId'),\n threadId: stringField(body, 'threadId'),\n resourceId: stringField(body, 'resourceId'),\n };\n\n const roomName =\n typeof options.roomName === 'function'\n ? options.roomName(args)\n : (options.roomName ?? `mastra-voice-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n const identity =\n typeof options.participantIdentity === 'function'\n ? options.participantIdentity(args)\n : (options.participantIdentity ?? metadata.resourceId ?? `user-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n // One memory thread per room unless the caller pins a thread explicitly.\n metadata.threadId ??= roomName;\n\n const recording =\n typeof options.recording === 'function' ? await options.recording({ ...args, roomName }) : options.recording;\n if (recording !== undefined) {\n try {\n await dispatchVoiceSession({\n roomName,\n agentName: options.agentName,\n metadata,\n serverUrl,\n apiKey,\n apiSecret,\n recording,\n });\n } catch (error) {\n if (error instanceof LiveKitRecordingRoomConflictError) {\n return c.json({ error: 'Recording requires a new room. Use a unique roomName for each call.' }, 409);\n }\n throw error;\n }\n }\n\n const token = new AccessToken(apiKey, apiSecret, { identity, ttl: options.ttl ?? '15m' });\n token.addGrant({\n room: roomName,\n roomJoin: true,\n canPublish: true,\n canSubscribe: true,\n canPublishData: true,\n canUpdateOwnMetadata: true,\n });\n // A pre-created room needs explicit dispatch; token roomConfig only applies at room creation.\n if (recording === undefined) {\n token.roomConfig = new RoomConfiguration({\n agents: [\n new RoomAgentDispatch({\n agentName: options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME,\n metadata: serializeSessionMetadata(metadata),\n }),\n ],\n });\n }\n\n const details: LiveKitConnectionDetails = {\n serverUrl,\n roomName,\n participantName: identity,\n participantToken: await token.toJwt(),\n };\n return c.json(details);\n };\n\n return {\n // Studio calls this default path and /api/system/packages reports it — change all three together.\n path: options.path ?? '/voice/livekit/connection-details',\n method: 'POST',\n requiresAuth: options.requiresAuth,\n handler,\n } as ApiRoute;\n}\n","import type { ApiRoute, ContextWithMastra } from '@mastra/core/server';\nimport type { SpanRecord } from '@mastra/core/storage';\nimport { z } from 'zod/v4';\nimport type { LiveKitRecording, LiveKitRecordingResponse } from './recording-types';\n\nconst traceIdSchema = z.string().min(1).max(256);\nexport const recordingSchema = z.object({\n url: z.url({ protocol: /^https?$/ }),\n expiresAt: z.string().datetime().optional(),\n}) satisfies z.ZodType<LiveKitRecording>;\n\nexport interface LiveKitRecordingResolverArgs {\n traceId: string;\n /** Taken from the stored voice call span, never from the browser request. */\n roomName: string;\n span: SpanRecord;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitRecordingRouteOptions {\n /** Studio discovers this route at its default path: /voice/livekit/recordings/:traceId. */\n path?: string;\n /** Defaults to true. Only disable for a local demo. */\n requiresAuth?: boolean;\n /** Required trace-specific access policy. Only true permits trace lookup and URL resolution. */\n authorize: (args: { traceId: string; context: ContextWithMastra }) => boolean | Promise<boolean>;\n /** Return a playable URL, or undefined if the recording is not available yet. */\n resolveRecording: (args: LiveKitRecordingResolverArgs) => Promise<LiveKitRecording | undefined>;\n}\n\n/** Resolves a voice call trace to a recording without persisting signed URLs in traces. */\nexport function liveKitRecordingRoute(options: LiveKitRecordingRouteOptions): ApiRoute {\n if (typeof options.authorize !== 'function') {\n throw new Error('liveKitRecordingRoute requires an authorize callback.');\n }\n return {\n path: options.path ?? '/voice/livekit/recordings/:traceId',\n method: 'GET',\n requiresAuth: options.requiresAuth ?? true,\n handler: async (context: ContextWithMastra) => {\n context.header('Cache-Control', 'no-store');\n const input = traceIdSchema.safeParse(context.req.param('traceId'));\n if (!input.success) return context.json({ error: 'A valid trace ID is required.' }, 400);\n const traceId = input.data;\n try {\n if ((await options.authorize({ traceId, context })) !== true) {\n return context.json({ error: 'Access to this recording is denied.' }, 403);\n }\n const store = await context.get('mastra').getStorage()?.getStore('observability');\n if (!store) return context.json({ error: 'Trace storage is not configured.' }, 503);\n const trace = await store.getTrace({ traceId });\n const calls =\n trace?.spans.filter(\n span =>\n span.name === 'voice call' &&\n typeof span.metadata?.roomName === 'string' &&\n span.metadata.roomName.length > 0,\n ) ?? [];\n if (!calls.length) return context.json({ error: 'Voice call trace not found.' }, 404);\n if (calls.length !== 1) return context.json({ error: 'This trace contains multiple voice calls.' }, 409);\n const span = calls[0]!;\n const roomName = span.metadata?.roomName;\n if (typeof roomName !== 'string') return context.json({ error: 'Voice call trace not found.' }, 404);\n const recording = await options.resolveRecording({ traceId, roomName, span, context });\n if (!recording) return context.json({ status: 'unavailable' } satisfies LiveKitRecordingResponse);\n // Validate and strip extra fields so storage credentials cannot accidentally become response fields.\n const result = recordingSchema.safeParse(recording);\n if (!result.success) return context.json({ error: 'Unable to load the call recording.' }, 502);\n return context.json({ status: 'ready', ...result.data } satisfies LiveKitRecordingResponse);\n } catch {\n // Storage/provider errors can contain credentials or signed request details.\n return context.json({ error: 'Unable to load the call recording.' }, 502);\n }\n },\n };\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** A consent decision captured from the caller during a call. */\nexport interface ConsentGrant {\n /**\n * The consent item, matching a key in the worker's `configuration.consentPolicy` (e.g.\n * `'summaryStorage'`).\n */\n item: string;\n /** `true` if the caller agreed, `false` if they declined. */\n granted: boolean;\n /** The caller the grant belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread the grant was captured on, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface ConsentToolOptions {\n /**\n * Persist the caller's decision to YOUR system of record (database, CRM, consent ledger). Called\n * each time the caller answers a consent question — keep it quick, it runs inside the turn. This\n * tool owns extracting the caller's identity from the tool execution context; you own storage, so\n * consent lives in a durable, compliant place rather than an opaque plugin store. Read those\n * grants back at `onCallEnd` (or before any consent-gated step) to enforce the requirement.\n */\n onGrant: (grant: ConsentGrant) => void | Promise<void>;\n /** Tool id the agent calls. Defaults to `'recordConsent'`. */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /** Restrict the accepted consent items to this set (becomes an enum). Defaults to any string. */\n items?: readonly string[];\n}\n\nconst DEFAULT_DESCRIPTION =\n \"Record the caller's consent decision for a data-processing item (for example, storing a summary \" +\n 'of the call). Call this as soon as the caller answers a consent question: granted = true if they ' +\n 'agree, false if they decline. Do not proceed with an activity that requires consent until you ' +\n 'have recorded the caller granting it.';\n\n/**\n * Builds a Mastra tool that captures the caller's consent decisions at runtime — the companion to\n * the worker's `configuration.consentPolicy`, which only DECLARES which consents a call needs. Add\n * it to the agent that answers the call; on each call the tool reads the caller's `resourceId` /\n * `threadId` from its execution context and hands the decision to your `onGrant` store. The runtime\n * grants live only where `onGrant` put them: enforce consent by reading them back from your own\n * store — at `onCallEnd` or before any consent-gated action — cross-checking against the declared\n * requirements in `VoiceCallEndArgs.configuration.consentPolicy` (the configuration carries the\n * policy, never the grants).\n *\n * ```ts\n * // agent tools\n * recordConsent: createConsentTool({\n * items: ['summaryStorage'],\n * onGrant: async ({ item, granted, resourceId }) => {\n * if (resourceId) await db.saveConsent(resourceId, item, granted);\n * },\n * }),\n * ```\n */\nexport function createConsentTool(options: ConsentToolOptions) {\n const items = options.items;\n const itemSchema = items && items.length > 0 ? z.enum([...items] as [string, ...string[]]) : z.string();\n return createTool({\n id: options.id ?? 'recordConsent',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n item: itemSchema.describe(\n \"The consent item the caller is answering for (matches the deployment's required consents).\",\n ),\n granted: z.boolean().describe('True if the caller agreed, false if they declined.'),\n }),\n execute: async ({ item, granted }, { agent }) => {\n await options.onGrant({ item, granted, resourceId: agent?.resourceId, threadId: agent?.threadId });\n return { recorded: true as const, item, granted };\n },\n });\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** Details of the agent's decision to end the call, handed to {@link EndCallToolOptions.onEndCall}. */\nexport interface EndCallRequest {\n /** Short reason the agent gave for ending the call, if any (e.g. \"caller said goodbye\"). */\n reason?: string;\n /** The caller the call belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface EndCallToolOptions {\n /**\n * Tool id the agent calls to end the call. Must match the worker's\n * `configuration.endCall.tool` (both default to `'endCall'`), since that's the name the worker\n * watches for to hang up.\n */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /**\n * Optional bookkeeping hook, called when the agent invokes the tool (records the reason, marks\n * the call resolved in your CRM, …). Runs inside the turn — keep it quick. It does NOT hang up\n * the call; the worker does that once the agent's final words finish playing. The tool reads the\n * caller's identity from its execution context and passes it here.\n */\n onEndCall?: (request: EndCallRequest) => void | Promise<void>;\n}\n\nconst DEFAULT_DESCRIPTION =\n 'End the phone call. Call this only after you have said goodbye and there is nothing left to do — ' +\n 'for example the caller says goodbye, or the task is complete and you have wrapped up. Say your ' +\n 'closing line first, then call this as your final action; the call hangs up once your words finish ' +\n 'playing. Your closing line must be a final statement, never a question — if you are asking the ' +\n 'caller anything (for example whether they need anything else), do NOT call this in that reply; ' +\n 'wait for their answer. Do not call it if the caller still needs something.';\n\n/**\n * Builds a Mastra tool the agent calls to end the call itself (say goodbye → hang up). It is the\n * agent-visible half of agent-initiated hang-up: the tool only SIGNALS intent (and runs optional\n * bookkeeping) — it can't reach the LiveKit room from inside `agent.stream()`. The worker owns the\n * hang-up: with `configuration.endCall` set, it watches each turn for this tool, waits for the\n * agent's closing words to finish playing, then disconnects (running `onCallEnd` on the way out).\n * Keeping the tool inside the single agent means it stays visible/editable in Studio and works on\n * the agent and workflow reply paths alike.\n *\n * ```ts\n * // agent tools\n * endCall: createEndCallTool({\n * onEndCall: ({ reason, resourceId }) => log.info('agent ended call', { reason, resourceId }),\n * }),\n * // worker\n * createLiveKitWorker({ mastra, agent: 'support', configuration: { endCall: {} } });\n * ```\n */\nexport function createEndCallTool(options: EndCallToolOptions = {}) {\n return createTool({\n id: options.id ?? 'endCall',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n reason: z\n .string()\n .nullish()\n .describe('Short reason the call is ending, e.g. \"caller said goodbye\" or \"task complete\".'),\n }),\n outputSchema: z.object({ ended: z.boolean() }),\n execute: async ({ reason }, { agent }) => {\n try {\n await options.onEndCall?.({\n reason: reason ?? undefined,\n resourceId: agent?.resourceId,\n threadId: agent?.threadId,\n });\n } catch (error) {\n // Bookkeeping is best-effort — the tool must still signal `{ ended: true }` so the worker's\n // end-call detector runs and the call actually hangs up.\n console.warn('@mastra/livekit: onEndCall hook threw', error);\n }\n return { ended: true as const };\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["RoomServiceClient","RoomEgress","serializeSessionMetadata","AgentDispatchClient","AccessToken","RoomConfiguration","RoomAgentDispatch","serializeSessionMetadata","z","DEFAULT_DESCRIPTION","z","createTool","createTool","z"],"sources":["../src/recording.ts","../src/dispatch.ts","../src/routes.ts","../src/recording-route.ts","../src/consent.ts","../src/end-call.ts"],"sourcesContent":["import { RoomEgress, RoomServiceClient } from 'livekit-server-sdk';\n\n/**\n * LiveKit auto-egress settings, accepted as a plain object or a RoomEgress instance.\n * Use `room.audioOnly` and `room.fileOutputs` for a single mixed audio recording.\n * Storage credentials belong here on the server, never in session metadata or tokens.\n */\nexport type LiveKitRecordingOptions = NonNullable<ConstructorParameters<typeof RoomEgress>[0]>;\n\n/** A recording-enabled session cannot reuse an existing LiveKit room. */\nexport class LiveKitRecordingRoomConflictError extends Error {\n readonly roomName: string;\n\n constructor(roomName: string) {\n super(\n `@mastra/livekit: recording requires a new room, but room \"${roomName}\" already exists. Use a fresh room name.`,\n );\n this.name = 'LiveKitRecordingRoomConflictError';\n this.roomName = roomName;\n }\n}\n\n/** Creates a fresh room with recording configured before any agent or caller joins. */\nexport async function createRecordingRoom(options: {\n roomName: string;\n serverUrl: string;\n apiKey: string;\n apiSecret: string;\n recording: LiveKitRecordingOptions;\n}) {\n const { recording, roomName } = options;\n if (!recording.room && !recording.participant && !recording.tracks) {\n throw new Error('@mastra/livekit: recording must configure at least one of room, participant, or tracks.');\n }\n\n const client = new RoomServiceClient(options.serverUrl.replace(/^ws/, 'http'), options.apiKey, options.apiSecret);\n // CreateRoom reuses existing rooms without applying room-composite auto-egress.\n // This preflight is not atomic: callers must use a fresh name and serialize room creation.\n const rooms = await client.listRooms([roomName]);\n if (rooms.length > 0) {\n throw new LiveKitRecordingRoomConflictError(roomName);\n }\n\n return client.createRoom({ name: roomName, egress: new RoomEgress(recording) });\n}\n","import { AgentDispatchClient } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { createRecordingRoom } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\nexport interface DispatchVoiceSessionOptions {\n /**\n * Room to dispatch the agent into (created on demand). With recording enabled, use a unique\n * name for each call. An existing room throws LiveKitRecordingRoomConflictError.\n */\n roomName: string;\n /** Must match the worker's `agentName`. Defaults to `'mastra-voice'`. */\n agentName?: string;\n metadata?: LiveKitSessionMetadata;\n /** Create a fresh room with automatic recording before dispatch. Omit to keep dispatch-only behavior. */\n recording?: LiveKitRecordingOptions;\n /** Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n}\n\nfunction toHttpUrl(url: string): string {\n return url.replace(/^ws/, 'http');\n}\n\n/**\n * Programmatically dispatches a Mastra voice agent into a LiveKit room — for\n * server-initiated sessions such as outbound calls or joining an existing room.\n */\nexport async function dispatchVoiceSession(options: DispatchVoiceSessionOptions) {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl) {\n throw new Error('@mastra/livekit: set LIVEKIT_URL or pass serverUrl to dispatchVoiceSession.');\n }\n if (!apiKey || !apiSecret) {\n throw new Error(\n '@mastra/livekit: set LIVEKIT_API_KEY and LIVEKIT_API_SECRET or pass apiKey/apiSecret to dispatchVoiceSession.',\n );\n }\n const metadata = serializeSessionMetadata(options.metadata ?? {});\n if (options.recording !== undefined) {\n await createRecordingRoom({\n roomName: options.roomName,\n serverUrl,\n apiKey,\n apiSecret,\n recording: options.recording,\n });\n }\n const client = new AgentDispatchClient(toHttpUrl(serverUrl), apiKey, apiSecret);\n return client.createDispatch(options.roomName, options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME, {\n metadata,\n });\n}\n","import { RoomAgentDispatch, RoomConfiguration } from '@livekit/protocol';\nimport type { ContextWithMastra, ApiRoute } from '@mastra/core/server';\nimport { AccessToken } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { dispatchVoiceSession } from './dispatch';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { LiveKitRecordingRoomConflictError } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\n/** Response body of the connection-details route. Matches LiveKit's frontend starter contract. */\nexport interface LiveKitConnectionDetails {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n}\n\nexport interface ConnectionRequestArgs {\n body: Record<string, unknown>;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitConnectionRouteOptions {\n /**\n * Route path. Defaults to `/voice/livekit/connection-details`. Mastra reserves the\n * `/api` prefix for built-in routes, so custom paths must not start with `/api`.\n */\n path?: string;\n /** LiveKit server URL (`wss://...`). Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n /** LiveKit agent name for explicit dispatch. Must match the worker's `agentName`. */\n agentName?: string;\n /** Token time-to-live. Defaults to `'15m'`. */\n ttl?: string | number;\n /** Defaults to `true` (Mastra custom routes require auth unless opted out). */\n requiresAuth?: boolean;\n /**\n * Room name or a function that derives one from the request. Defaults to a generated name.\n * With recording enabled, use a unique name for each call. An existing room returns HTTP 409\n * before agent dispatch or token issuance.\n */\n roomName?: string | ((args: ConnectionRequestArgs) => string);\n participantIdentity?: string | ((args: ConnectionRequestArgs) => string);\n /**\n * Session metadata delivered to the worker via agent dispatch. Defaults to passing\n * through `agentId`, `threadId`, and `resourceId` from the request body.\n */\n metadata?: (args: ConnectionRequestArgs) => LiveKitSessionMetadata | Promise<LiveKitSessionMetadata>;\n /**\n * Create a fresh room with automatic recording and dispatch the agent before returning a token.\n * The server-side callback receives the resolved room name; return undefined to skip recording.\n * Recording settings and storage credentials are never included in the participant token.\n */\n recording?:\n | LiveKitRecordingOptions\n | ((\n args: ConnectionRequestArgs & { roomName: string },\n ) => LiveKitRecordingOptions | undefined | Promise<LiveKitRecordingOptions | undefined>);\n}\n\nfunction stringField(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * An ApiRoute for `server.apiRoutes` that mints a LiveKit access token with the Mastra\n * voice agent dispatched into the room. Frontends call it to join a voice session:\n *\n * ```ts\n * export const mastra = new Mastra({\n * agents: { support },\n * server: { apiRoutes: [liveKitConnectionRoute({ agentName: 'mastra-voice' })] },\n * });\n * ```\n */\nexport function liveKitConnectionRoute(options: LiveKitConnectionRouteOptions = {}): ApiRoute {\n const handler = async (c: ContextWithMastra) => {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl || !apiKey || !apiSecret) {\n return c.json(\n {\n error:\n 'LiveKit is not configured. Set LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET ' +\n '(or pass serverUrl/apiKey/apiSecret to liveKitConnectionRoute).',\n },\n 500,\n );\n }\n\n const body: Record<string, unknown> = await c.req\n .json()\n .then((parsed: unknown) =>\n parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {},\n )\n .catch(() => ({}));\n const args: ConnectionRequestArgs = { body, context: c };\n\n const metadata: LiveKitSessionMetadata = options.metadata\n ? await options.metadata(args)\n : {\n agentId: stringField(body, 'agentId'),\n threadId: stringField(body, 'threadId'),\n resourceId: stringField(body, 'resourceId'),\n };\n\n const roomName =\n typeof options.roomName === 'function'\n ? options.roomName(args)\n : (options.roomName ?? `mastra-voice-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n const identity =\n typeof options.participantIdentity === 'function'\n ? options.participantIdentity(args)\n : (options.participantIdentity ?? metadata.resourceId ?? `user-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n // One memory thread per room unless the caller pins a thread explicitly.\n metadata.threadId ??= roomName;\n\n const recording =\n typeof options.recording === 'function' ? await options.recording({ ...args, roomName }) : options.recording;\n if (recording !== undefined) {\n try {\n await dispatchVoiceSession({\n roomName,\n agentName: options.agentName,\n metadata,\n serverUrl,\n apiKey,\n apiSecret,\n recording,\n });\n } catch (error) {\n if (error instanceof LiveKitRecordingRoomConflictError) {\n return c.json({ error: 'Recording requires a new room. Use a unique roomName for each call.' }, 409);\n }\n throw error;\n }\n }\n\n const token = new AccessToken(apiKey, apiSecret, { identity, ttl: options.ttl ?? '15m' });\n token.addGrant({\n room: roomName,\n roomJoin: true,\n canPublish: true,\n canSubscribe: true,\n canPublishData: true,\n canUpdateOwnMetadata: true,\n });\n // A pre-created room needs explicit dispatch; token roomConfig only applies at room creation.\n if (recording === undefined) {\n token.roomConfig = new RoomConfiguration({\n agents: [\n new RoomAgentDispatch({\n agentName: options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME,\n metadata: serializeSessionMetadata(metadata),\n }),\n ],\n });\n }\n\n const details: LiveKitConnectionDetails = {\n serverUrl,\n roomName,\n participantName: identity,\n participantToken: await token.toJwt(),\n };\n return c.json(details);\n };\n\n return {\n // Studio calls this default path and /api/system/packages reports it — change all three together.\n path: options.path ?? '/voice/livekit/connection-details',\n method: 'POST',\n requiresAuth: options.requiresAuth,\n handler,\n } as ApiRoute;\n}\n","import type { ApiRoute, ContextWithMastra } from '@mastra/core/server';\nimport type { SpanRecord } from '@mastra/core/storage';\nimport { z } from 'zod/v4';\nimport type { LiveKitRecording, LiveKitRecordingResponse } from './recording-types';\n\nconst traceIdSchema = z.string().min(1).max(256);\nexport const recordingSchema = z.object({\n url: z.url({ protocol: /^https?$/ }),\n expiresAt: z.string().datetime().optional(),\n}) satisfies z.ZodType<LiveKitRecording>;\n\nexport interface LiveKitRecordingResolverArgs {\n traceId: string;\n /** Taken from the stored voice call span, never from the browser request. */\n roomName: string;\n span: SpanRecord;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitRecordingRouteOptions {\n /** Studio discovers this route at its default path: /voice/livekit/recordings/:traceId. */\n path?: string;\n /** Defaults to true. Only disable for a local demo. */\n requiresAuth?: boolean;\n /** Required trace-specific access policy. Only true permits trace lookup and URL resolution. */\n authorize: (args: { traceId: string; context: ContextWithMastra }) => boolean | Promise<boolean>;\n /** Return a playable URL, or undefined if the recording is not available yet. */\n resolveRecording: (args: LiveKitRecordingResolverArgs) => Promise<LiveKitRecording | undefined>;\n}\n\n/** Resolves a voice call trace to a recording without persisting signed URLs in traces. */\nexport function liveKitRecordingRoute(options: LiveKitRecordingRouteOptions): ApiRoute {\n if (typeof options.authorize !== 'function') {\n throw new Error('liveKitRecordingRoute requires an authorize callback.');\n }\n return {\n path: options.path ?? '/voice/livekit/recordings/:traceId',\n method: 'GET',\n requiresAuth: options.requiresAuth ?? true,\n handler: async (context: ContextWithMastra) => {\n context.header('Cache-Control', 'no-store');\n const input = traceIdSchema.safeParse(context.req.param('traceId'));\n if (!input.success) return context.json({ error: 'A valid trace ID is required.' }, 400);\n const traceId = input.data;\n try {\n if ((await options.authorize({ traceId, context })) !== true) {\n return context.json({ error: 'Access to this recording is denied.' }, 403);\n }\n const store = await context.get('mastra').getStorage()?.getStore('observability');\n if (!store) return context.json({ error: 'Trace storage is not configured.' }, 503);\n const trace = await store.getTrace({ traceId });\n const calls =\n trace?.spans.filter(\n span =>\n span.name === 'voice call' &&\n typeof span.metadata?.roomName === 'string' &&\n span.metadata.roomName.length > 0,\n ) ?? [];\n if (!calls.length) return context.json({ error: 'Voice call trace not found.' }, 404);\n if (calls.length !== 1) return context.json({ error: 'This trace contains multiple voice calls.' }, 409);\n const span = calls[0]!;\n const roomName = span.metadata?.roomName;\n if (typeof roomName !== 'string') return context.json({ error: 'Voice call trace not found.' }, 404);\n const recording = await options.resolveRecording({ traceId, roomName, span, context });\n if (!recording) return context.json({ status: 'unavailable' } satisfies LiveKitRecordingResponse);\n // Validate and strip extra fields so storage credentials cannot accidentally become response fields.\n const result = recordingSchema.safeParse(recording);\n if (!result.success) return context.json({ error: 'Unable to load the call recording.' }, 502);\n return context.json({ status: 'ready', ...result.data } satisfies LiveKitRecordingResponse);\n } catch {\n // Storage/provider errors can contain credentials or signed request details.\n return context.json({ error: 'Unable to load the call recording.' }, 502);\n }\n },\n };\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** A consent decision captured from the caller during a call. */\nexport interface ConsentGrant {\n /**\n * The consent item, matching a key in the worker's `configuration.consentPolicy` (e.g.\n * `'summaryStorage'`).\n */\n item: string;\n /** `true` if the caller agreed, `false` if they declined. */\n granted: boolean;\n /** The caller the grant belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread the grant was captured on, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface ConsentToolOptions {\n /**\n * Persist the caller's decision to YOUR system of record (database, CRM, consent ledger). Called\n * each time the caller answers a consent question — keep it quick, it runs inside the turn. This\n * tool owns extracting the caller's identity from the tool execution context; you own storage, so\n * consent lives in a durable, compliant place rather than an opaque plugin store. Read those\n * grants back at `onCallEnd` (or before any consent-gated step) to enforce the requirement.\n */\n onGrant: (grant: ConsentGrant) => void | Promise<void>;\n /** Tool id the agent calls. Defaults to `'recordConsent'`. */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /** Restrict the accepted consent items to this set (becomes an enum). Defaults to any string. */\n items?: readonly string[];\n}\n\nconst DEFAULT_DESCRIPTION =\n \"Record the caller's consent decision for a data-processing item (for example, storing a summary \" +\n 'of the call). Call this as soon as the caller answers a consent question: granted = true if they ' +\n 'agree, false if they decline. Do not proceed with an activity that requires consent until you ' +\n 'have recorded the caller granting it.';\n\n/**\n * Builds a Mastra tool that captures the caller's consent decisions at runtime — the companion to\n * the worker's `configuration.consentPolicy`, which only DECLARES which consents a call needs. Add\n * it to the agent that answers the call; on each call the tool reads the caller's `resourceId` /\n * `threadId` from its execution context and hands the decision to your `onGrant` store. The runtime\n * grants live only where `onGrant` put them: enforce consent by reading them back from your own\n * store — at `onCallEnd` or before any consent-gated action — cross-checking against the declared\n * requirements in `VoiceCallEndArgs.configuration.consentPolicy` (the configuration carries the\n * policy, never the grants).\n *\n * ```ts\n * // agent tools\n * recordConsent: createConsentTool({\n * items: ['summaryStorage'],\n * onGrant: async ({ item, granted, resourceId }) => {\n * if (resourceId) await db.saveConsent(resourceId, item, granted);\n * },\n * }),\n * ```\n */\nexport function createConsentTool(options: ConsentToolOptions) {\n const items = options.items;\n const itemSchema = items && items.length > 0 ? z.enum([...items] as [string, ...string[]]) : z.string();\n return createTool({\n id: options.id ?? 'recordConsent',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n item: itemSchema.describe(\n \"The consent item the caller is answering for (matches the deployment's required consents).\",\n ),\n granted: z.boolean().describe('True if the caller agreed, false if they declined.'),\n }),\n execute: async ({ item, granted }, { agent }) => {\n await options.onGrant({ item, granted, resourceId: agent?.resourceId, threadId: agent?.threadId });\n return { recorded: true as const, item, granted };\n },\n });\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** Details of the agent's decision to end the call, handed to {@link EndCallToolOptions.onEndCall}. */\nexport interface EndCallRequest {\n /** Short reason the agent gave for ending the call, if any (e.g. \"caller said goodbye\"). */\n reason?: string;\n /** The caller the call belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface EndCallToolOptions {\n /**\n * Tool id the agent calls to end the call. Must match the worker's\n * `configuration.endCall.tool` (both default to `'endCall'`), since that's the name the worker\n * watches for to hang up.\n */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /**\n * Optional bookkeeping hook, called when the agent invokes the tool (records the reason, marks\n * the call resolved in your CRM, …). Runs inside the turn — keep it quick. It does NOT hang up\n * the call; the worker does that once the agent's final words finish playing. The tool reads the\n * caller's identity from its execution context and passes it here.\n */\n onEndCall?: (request: EndCallRequest) => void | Promise<void>;\n}\n\nconst DEFAULT_DESCRIPTION =\n 'End the phone call. Call this only after you have said goodbye and there is nothing left to do — ' +\n 'for example the caller says goodbye, or the task is complete and you have wrapped up. Say your ' +\n 'closing line first, then call this as your final action; the call hangs up once your words finish ' +\n 'playing. Your closing line must be a final statement, never a question — if you are asking the ' +\n 'caller anything (for example whether they need anything else), do NOT call this in that reply; ' +\n 'wait for their answer. Do not call it if the caller still needs something.';\n\n/**\n * Builds a Mastra tool the agent calls to end the call itself (say goodbye → hang up). It is the\n * agent-visible half of agent-initiated hang-up: the tool only SIGNALS intent (and runs optional\n * bookkeeping) — it can't reach the LiveKit room from inside `agent.stream()`. The worker owns the\n * hang-up: with `configuration.endCall` set, it watches each turn for this tool, waits for the\n * agent's closing words to finish playing, then disconnects (running `onCallEnd` on the way out).\n * Keeping the tool inside the single agent means it stays visible/editable in Studio and works on\n * the agent and workflow reply paths alike.\n *\n * ```ts\n * // agent tools\n * endCall: createEndCallTool({\n * onEndCall: ({ reason, resourceId }) => log.info('agent ended call', { reason, resourceId }),\n * }),\n * // worker\n * createLiveKitWorker({ mastra, agent: 'support', configuration: { endCall: {} } });\n * ```\n */\nexport function createEndCallTool(options: EndCallToolOptions = {}) {\n return createTool({\n id: options.id ?? 'endCall',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n reason: z\n .string()\n .nullish()\n .describe('Short reason the call is ending, e.g. \"caller said goodbye\" or \"task complete\".'),\n }),\n outputSchema: z.object({ ended: z.boolean() }),\n execute: async ({ reason }, { agent }) => {\n try {\n await options.onEndCall?.({\n reason: reason ?? undefined,\n resourceId: agent?.resourceId,\n threadId: agent?.threadId,\n });\n } catch (error) {\n // Bookkeeping is best-effort — the tool must still signal `{ ended: true }` so the worker's\n // end-call detector runs and the call actually hangs up.\n console.warn('@mastra/livekit: onEndCall hook threw', error);\n }\n return { ended: true as const };\n },\n });\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { a as DEFAULT_LIVEKIT_AGENT_NAME, i as serializeSessionMetadata, n as pipeAgentReplyToWriter } from "./workflow-generator-5nHqlZV7.js";
1
+ import { a as DEFAULT_LIVEKIT_AGENT_NAME, i as serializeSessionMetadata, n as pipeAgentReplyToWriter } from "./workflow-generator-j3_TfX1B.js";
2
2
  import { RoomAgentDispatch, RoomConfiguration } from "@livekit/protocol";
3
3
  import { AccessToken, AgentDispatchClient, RoomEgress, RoomServiceClient } from "livekit-server-sdk";
4
4
  import { z } from "zod/v4";
5
5
  import { createTool } from "@mastra/core/tools";
6
6
  import { z as z$1 } from "zod";
7
- //#region src/recording.ts
7
+ // src/recording.ts
8
8
  /** A recording-enabled session cannot reuse an existing LiveKit room. */
9
9
  var LiveKitRecordingRoomConflictError = class extends Error {
10
10
  roomName;
@@ -25,8 +25,7 @@ async function createRecordingRoom(options) {
25
25
  egress: new RoomEgress(recording)
26
26
  });
27
27
  }
28
- //#endregion
29
- //#region src/dispatch.ts
28
+ // src/dispatch.ts
30
29
  function toHttpUrl(url) {
31
30
  return url.replace(/^ws/, "http");
32
31
  }
@@ -50,8 +49,7 @@ async function dispatchVoiceSession(options) {
50
49
  });
51
50
  return new AgentDispatchClient(toHttpUrl(serverUrl), apiKey, apiSecret).createDispatch(options.roomName, options.agentName ?? "mastra-voice", { metadata });
52
51
  }
53
- //#endregion
54
- //#region src/routes.ts
52
+ // src/routes.ts
55
53
  function stringField(body, key) {
56
54
  const value = body[key];
57
55
  return typeof value === "string" && value.length > 0 ? value : void 0;
@@ -135,8 +133,7 @@ function liveKitConnectionRoute(options = {}) {
135
133
  handler
136
134
  };
137
135
  }
138
- //#endregion
139
- //#region src/recording-route.ts
136
+ // src/recording-route.ts
140
137
  const traceIdSchema = z.string().min(1).max(256);
141
138
  const recordingSchema = z.object({
142
139
  url: z.url({ protocol: /^https?$/ }),
@@ -186,8 +183,7 @@ function liveKitRecordingRoute(options) {
186
183
  }
187
184
  };
188
185
  }
189
- //#endregion
190
- //#region src/consent.ts
186
+ // src/consent.ts
191
187
  const DEFAULT_DESCRIPTION$1 = "Record the caller's consent decision for a data-processing item (for example, storing a summary of the call). Call this as soon as the caller answers a consent question: granted = true if they agree, false if they decline. Do not proceed with an activity that requires consent until you have recorded the caller granting it.";
192
188
  /**
193
189
  * Builds a Mastra tool that captures the caller's consent decisions at runtime — the companion to
@@ -234,8 +230,7 @@ function createConsentTool(options) {
234
230
  }
235
231
  });
236
232
  }
237
- //#endregion
238
- //#region src/end-call.ts
233
+ // src/end-call.ts
239
234
  const DEFAULT_DESCRIPTION = "End the phone call. Call this only after you have said goodbye and there is nothing left to do — for example the caller says goodbye, or the task is complete and you have wrapped up. Say your closing line first, then call this as your final action; the call hangs up once your words finish playing. Your closing line must be a final statement, never a question — if you are asking the caller anything (for example whether they need anything else), do NOT call this in that reply; wait for their answer. Do not call it if the caller still needs something.";
240
235
  /**
241
236
  * Builds a Mastra tool the agent calls to end the call itself (say goodbye → hang up). It is the
@@ -275,7 +270,6 @@ function createEndCallTool(options = {}) {
275
270
  }
276
271
  });
277
272
  }
278
- //#endregion
279
273
  export { DEFAULT_LIVEKIT_AGENT_NAME, LiveKitRecordingRoomConflictError, createConsentTool, createEndCallTool, dispatchVoiceSession, liveKitConnectionRoute, liveKitRecordingRoute, pipeAgentReplyToWriter, serializeSessionMetadata };
280
274
 
281
275
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["DEFAULT_DESCRIPTION","z","z"],"sources":["../src/recording.ts","../src/dispatch.ts","../src/routes.ts","../src/recording-route.ts","../src/consent.ts","../src/end-call.ts"],"sourcesContent":["import { RoomEgress, RoomServiceClient } from 'livekit-server-sdk';\n\n/**\n * LiveKit auto-egress settings, accepted as a plain object or a RoomEgress instance.\n * Use `room.audioOnly` and `room.fileOutputs` for a single mixed audio recording.\n * Storage credentials belong here on the server, never in session metadata or tokens.\n */\nexport type LiveKitRecordingOptions = NonNullable<ConstructorParameters<typeof RoomEgress>[0]>;\n\n/** A recording-enabled session cannot reuse an existing LiveKit room. */\nexport class LiveKitRecordingRoomConflictError extends Error {\n readonly roomName: string;\n\n constructor(roomName: string) {\n super(\n `@mastra/livekit: recording requires a new room, but room \"${roomName}\" already exists. Use a fresh room name.`,\n );\n this.name = 'LiveKitRecordingRoomConflictError';\n this.roomName = roomName;\n }\n}\n\n/** Creates a fresh room with recording configured before any agent or caller joins. */\nexport async function createRecordingRoom(options: {\n roomName: string;\n serverUrl: string;\n apiKey: string;\n apiSecret: string;\n recording: LiveKitRecordingOptions;\n}) {\n const { recording, roomName } = options;\n if (!recording.room && !recording.participant && !recording.tracks) {\n throw new Error('@mastra/livekit: recording must configure at least one of room, participant, or tracks.');\n }\n\n const client = new RoomServiceClient(options.serverUrl.replace(/^ws/, 'http'), options.apiKey, options.apiSecret);\n // CreateRoom reuses existing rooms without applying room-composite auto-egress.\n // This preflight is not atomic: callers must use a fresh name and serialize room creation.\n const rooms = await client.listRooms([roomName]);\n if (rooms.length > 0) {\n throw new LiveKitRecordingRoomConflictError(roomName);\n }\n\n return client.createRoom({ name: roomName, egress: new RoomEgress(recording) });\n}\n","import { AgentDispatchClient } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { createRecordingRoom } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\nexport interface DispatchVoiceSessionOptions {\n /**\n * Room to dispatch the agent into (created on demand). With recording enabled, use a unique\n * name for each call. An existing room throws LiveKitRecordingRoomConflictError.\n */\n roomName: string;\n /** Must match the worker's `agentName`. Defaults to `'mastra-voice'`. */\n agentName?: string;\n metadata?: LiveKitSessionMetadata;\n /** Create a fresh room with automatic recording before dispatch. Omit to keep dispatch-only behavior. */\n recording?: LiveKitRecordingOptions;\n /** Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n}\n\nfunction toHttpUrl(url: string): string {\n return url.replace(/^ws/, 'http');\n}\n\n/**\n * Programmatically dispatches a Mastra voice agent into a LiveKit room — for\n * server-initiated sessions such as outbound calls or joining an existing room.\n */\nexport async function dispatchVoiceSession(options: DispatchVoiceSessionOptions) {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl) {\n throw new Error('@mastra/livekit: set LIVEKIT_URL or pass serverUrl to dispatchVoiceSession.');\n }\n if (!apiKey || !apiSecret) {\n throw new Error(\n '@mastra/livekit: set LIVEKIT_API_KEY and LIVEKIT_API_SECRET or pass apiKey/apiSecret to dispatchVoiceSession.',\n );\n }\n const metadata = serializeSessionMetadata(options.metadata ?? {});\n if (options.recording !== undefined) {\n await createRecordingRoom({\n roomName: options.roomName,\n serverUrl,\n apiKey,\n apiSecret,\n recording: options.recording,\n });\n }\n const client = new AgentDispatchClient(toHttpUrl(serverUrl), apiKey, apiSecret);\n return client.createDispatch(options.roomName, options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME, {\n metadata,\n });\n}\n","import { RoomAgentDispatch, RoomConfiguration } from '@livekit/protocol';\nimport type { ContextWithMastra, ApiRoute } from '@mastra/core/server';\nimport { AccessToken } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { dispatchVoiceSession } from './dispatch';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { LiveKitRecordingRoomConflictError } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\n/** Response body of the connection-details route. Matches LiveKit's frontend starter contract. */\nexport interface LiveKitConnectionDetails {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n}\n\nexport interface ConnectionRequestArgs {\n body: Record<string, unknown>;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitConnectionRouteOptions {\n /**\n * Route path. Defaults to `/voice/livekit/connection-details`. Mastra reserves the\n * `/api` prefix for built-in routes, so custom paths must not start with `/api`.\n */\n path?: string;\n /** LiveKit server URL (`wss://...`). Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n /** LiveKit agent name for explicit dispatch. Must match the worker's `agentName`. */\n agentName?: string;\n /** Token time-to-live. Defaults to `'15m'`. */\n ttl?: string | number;\n /** Defaults to `true` (Mastra custom routes require auth unless opted out). */\n requiresAuth?: boolean;\n /**\n * Room name or a function that derives one from the request. Defaults to a generated name.\n * With recording enabled, use a unique name for each call. An existing room returns HTTP 409\n * before agent dispatch or token issuance.\n */\n roomName?: string | ((args: ConnectionRequestArgs) => string);\n participantIdentity?: string | ((args: ConnectionRequestArgs) => string);\n /**\n * Session metadata delivered to the worker via agent dispatch. Defaults to passing\n * through `agentId`, `threadId`, and `resourceId` from the request body.\n */\n metadata?: (args: ConnectionRequestArgs) => LiveKitSessionMetadata | Promise<LiveKitSessionMetadata>;\n /**\n * Create a fresh room with automatic recording and dispatch the agent before returning a token.\n * The server-side callback receives the resolved room name; return undefined to skip recording.\n * Recording settings and storage credentials are never included in the participant token.\n */\n recording?:\n | LiveKitRecordingOptions\n | ((\n args: ConnectionRequestArgs & { roomName: string },\n ) => LiveKitRecordingOptions | undefined | Promise<LiveKitRecordingOptions | undefined>);\n}\n\nfunction stringField(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * An ApiRoute for `server.apiRoutes` that mints a LiveKit access token with the Mastra\n * voice agent dispatched into the room. Frontends call it to join a voice session:\n *\n * ```ts\n * export const mastra = new Mastra({\n * agents: { support },\n * server: { apiRoutes: [liveKitConnectionRoute({ agentName: 'mastra-voice' })] },\n * });\n * ```\n */\nexport function liveKitConnectionRoute(options: LiveKitConnectionRouteOptions = {}): ApiRoute {\n const handler = async (c: ContextWithMastra) => {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl || !apiKey || !apiSecret) {\n return c.json(\n {\n error:\n 'LiveKit is not configured. Set LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET ' +\n '(or pass serverUrl/apiKey/apiSecret to liveKitConnectionRoute).',\n },\n 500,\n );\n }\n\n const body: Record<string, unknown> = await c.req\n .json()\n .then((parsed: unknown) =>\n parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {},\n )\n .catch(() => ({}));\n const args: ConnectionRequestArgs = { body, context: c };\n\n const metadata: LiveKitSessionMetadata = options.metadata\n ? await options.metadata(args)\n : {\n agentId: stringField(body, 'agentId'),\n threadId: stringField(body, 'threadId'),\n resourceId: stringField(body, 'resourceId'),\n };\n\n const roomName =\n typeof options.roomName === 'function'\n ? options.roomName(args)\n : (options.roomName ?? `mastra-voice-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n const identity =\n typeof options.participantIdentity === 'function'\n ? options.participantIdentity(args)\n : (options.participantIdentity ?? metadata.resourceId ?? `user-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n // One memory thread per room unless the caller pins a thread explicitly.\n metadata.threadId ??= roomName;\n\n const recording =\n typeof options.recording === 'function' ? await options.recording({ ...args, roomName }) : options.recording;\n if (recording !== undefined) {\n try {\n await dispatchVoiceSession({\n roomName,\n agentName: options.agentName,\n metadata,\n serverUrl,\n apiKey,\n apiSecret,\n recording,\n });\n } catch (error) {\n if (error instanceof LiveKitRecordingRoomConflictError) {\n return c.json({ error: 'Recording requires a new room. Use a unique roomName for each call.' }, 409);\n }\n throw error;\n }\n }\n\n const token = new AccessToken(apiKey, apiSecret, { identity, ttl: options.ttl ?? '15m' });\n token.addGrant({\n room: roomName,\n roomJoin: true,\n canPublish: true,\n canSubscribe: true,\n canPublishData: true,\n canUpdateOwnMetadata: true,\n });\n // A pre-created room needs explicit dispatch; token roomConfig only applies at room creation.\n if (recording === undefined) {\n token.roomConfig = new RoomConfiguration({\n agents: [\n new RoomAgentDispatch({\n agentName: options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME,\n metadata: serializeSessionMetadata(metadata),\n }),\n ],\n });\n }\n\n const details: LiveKitConnectionDetails = {\n serverUrl,\n roomName,\n participantName: identity,\n participantToken: await token.toJwt(),\n };\n return c.json(details);\n };\n\n return {\n // Studio calls this default path and /api/system/packages reports it — change all three together.\n path: options.path ?? '/voice/livekit/connection-details',\n method: 'POST',\n requiresAuth: options.requiresAuth,\n handler,\n } as ApiRoute;\n}\n","import type { ApiRoute, ContextWithMastra } from '@mastra/core/server';\nimport type { SpanRecord } from '@mastra/core/storage';\nimport { z } from 'zod/v4';\nimport type { LiveKitRecording, LiveKitRecordingResponse } from './recording-types';\n\nconst traceIdSchema = z.string().min(1).max(256);\nexport const recordingSchema = z.object({\n url: z.url({ protocol: /^https?$/ }),\n expiresAt: z.string().datetime().optional(),\n}) satisfies z.ZodType<LiveKitRecording>;\n\nexport interface LiveKitRecordingResolverArgs {\n traceId: string;\n /** Taken from the stored voice call span, never from the browser request. */\n roomName: string;\n span: SpanRecord;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitRecordingRouteOptions {\n /** Studio discovers this route at its default path: /voice/livekit/recordings/:traceId. */\n path?: string;\n /** Defaults to true. Only disable for a local demo. */\n requiresAuth?: boolean;\n /** Required trace-specific access policy. Only true permits trace lookup and URL resolution. */\n authorize: (args: { traceId: string; context: ContextWithMastra }) => boolean | Promise<boolean>;\n /** Return a playable URL, or undefined if the recording is not available yet. */\n resolveRecording: (args: LiveKitRecordingResolverArgs) => Promise<LiveKitRecording | undefined>;\n}\n\n/** Resolves a voice call trace to a recording without persisting signed URLs in traces. */\nexport function liveKitRecordingRoute(options: LiveKitRecordingRouteOptions): ApiRoute {\n if (typeof options.authorize !== 'function') {\n throw new Error('liveKitRecordingRoute requires an authorize callback.');\n }\n return {\n path: options.path ?? '/voice/livekit/recordings/:traceId',\n method: 'GET',\n requiresAuth: options.requiresAuth ?? true,\n handler: async (context: ContextWithMastra) => {\n context.header('Cache-Control', 'no-store');\n const input = traceIdSchema.safeParse(context.req.param('traceId'));\n if (!input.success) return context.json({ error: 'A valid trace ID is required.' }, 400);\n const traceId = input.data;\n try {\n if ((await options.authorize({ traceId, context })) !== true) {\n return context.json({ error: 'Access to this recording is denied.' }, 403);\n }\n const store = await context.get('mastra').getStorage()?.getStore('observability');\n if (!store) return context.json({ error: 'Trace storage is not configured.' }, 503);\n const trace = await store.getTrace({ traceId });\n const calls =\n trace?.spans.filter(\n span =>\n span.name === 'voice call' &&\n typeof span.metadata?.roomName === 'string' &&\n span.metadata.roomName.length > 0,\n ) ?? [];\n if (!calls.length) return context.json({ error: 'Voice call trace not found.' }, 404);\n if (calls.length !== 1) return context.json({ error: 'This trace contains multiple voice calls.' }, 409);\n const span = calls[0]!;\n const roomName = span.metadata?.roomName;\n if (typeof roomName !== 'string') return context.json({ error: 'Voice call trace not found.' }, 404);\n const recording = await options.resolveRecording({ traceId, roomName, span, context });\n if (!recording) return context.json({ status: 'unavailable' } satisfies LiveKitRecordingResponse);\n // Validate and strip extra fields so storage credentials cannot accidentally become response fields.\n const result = recordingSchema.safeParse(recording);\n if (!result.success) return context.json({ error: 'Unable to load the call recording.' }, 502);\n return context.json({ status: 'ready', ...result.data } satisfies LiveKitRecordingResponse);\n } catch {\n // Storage/provider errors can contain credentials or signed request details.\n return context.json({ error: 'Unable to load the call recording.' }, 502);\n }\n },\n };\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** A consent decision captured from the caller during a call. */\nexport interface ConsentGrant {\n /**\n * The consent item, matching a key in the worker's `configuration.consentPolicy` (e.g.\n * `'summaryStorage'`).\n */\n item: string;\n /** `true` if the caller agreed, `false` if they declined. */\n granted: boolean;\n /** The caller the grant belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread the grant was captured on, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface ConsentToolOptions {\n /**\n * Persist the caller's decision to YOUR system of record (database, CRM, consent ledger). Called\n * each time the caller answers a consent question — keep it quick, it runs inside the turn. This\n * tool owns extracting the caller's identity from the tool execution context; you own storage, so\n * consent lives in a durable, compliant place rather than an opaque plugin store. Read those\n * grants back at `onCallEnd` (or before any consent-gated step) to enforce the requirement.\n */\n onGrant: (grant: ConsentGrant) => void | Promise<void>;\n /** Tool id the agent calls. Defaults to `'recordConsent'`. */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /** Restrict the accepted consent items to this set (becomes an enum). Defaults to any string. */\n items?: readonly string[];\n}\n\nconst DEFAULT_DESCRIPTION =\n \"Record the caller's consent decision for a data-processing item (for example, storing a summary \" +\n 'of the call). Call this as soon as the caller answers a consent question: granted = true if they ' +\n 'agree, false if they decline. Do not proceed with an activity that requires consent until you ' +\n 'have recorded the caller granting it.';\n\n/**\n * Builds a Mastra tool that captures the caller's consent decisions at runtime — the companion to\n * the worker's `configuration.consentPolicy`, which only DECLARES which consents a call needs. Add\n * it to the agent that answers the call; on each call the tool reads the caller's `resourceId` /\n * `threadId` from its execution context and hands the decision to your `onGrant` store. The runtime\n * grants live only where `onGrant` put them: enforce consent by reading them back from your own\n * store — at `onCallEnd` or before any consent-gated action — cross-checking against the declared\n * requirements in `VoiceCallEndArgs.configuration.consentPolicy` (the configuration carries the\n * policy, never the grants).\n *\n * ```ts\n * // agent tools\n * recordConsent: createConsentTool({\n * items: ['summaryStorage'],\n * onGrant: async ({ item, granted, resourceId }) => {\n * if (resourceId) await db.saveConsent(resourceId, item, granted);\n * },\n * }),\n * ```\n */\nexport function createConsentTool(options: ConsentToolOptions) {\n const items = options.items;\n const itemSchema = items && items.length > 0 ? z.enum([...items] as [string, ...string[]]) : z.string();\n return createTool({\n id: options.id ?? 'recordConsent',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n item: itemSchema.describe(\n \"The consent item the caller is answering for (matches the deployment's required consents).\",\n ),\n granted: z.boolean().describe('True if the caller agreed, false if they declined.'),\n }),\n execute: async ({ item, granted }, { agent }) => {\n await options.onGrant({ item, granted, resourceId: agent?.resourceId, threadId: agent?.threadId });\n return { recorded: true as const, item, granted };\n },\n });\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** Details of the agent's decision to end the call, handed to {@link EndCallToolOptions.onEndCall}. */\nexport interface EndCallRequest {\n /** Short reason the agent gave for ending the call, if any (e.g. \"caller said goodbye\"). */\n reason?: string;\n /** The caller the call belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface EndCallToolOptions {\n /**\n * Tool id the agent calls to end the call. Must match the worker's\n * `configuration.endCall.tool` (both default to `'endCall'`), since that's the name the worker\n * watches for to hang up.\n */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /**\n * Optional bookkeeping hook, called when the agent invokes the tool (records the reason, marks\n * the call resolved in your CRM, …). Runs inside the turn — keep it quick. It does NOT hang up\n * the call; the worker does that once the agent's final words finish playing. The tool reads the\n * caller's identity from its execution context and passes it here.\n */\n onEndCall?: (request: EndCallRequest) => void | Promise<void>;\n}\n\nconst DEFAULT_DESCRIPTION =\n 'End the phone call. Call this only after you have said goodbye and there is nothing left to do — ' +\n 'for example the caller says goodbye, or the task is complete and you have wrapped up. Say your ' +\n 'closing line first, then call this as your final action; the call hangs up once your words finish ' +\n 'playing. Your closing line must be a final statement, never a question — if you are asking the ' +\n 'caller anything (for example whether they need anything else), do NOT call this in that reply; ' +\n 'wait for their answer. Do not call it if the caller still needs something.';\n\n/**\n * Builds a Mastra tool the agent calls to end the call itself (say goodbye → hang up). It is the\n * agent-visible half of agent-initiated hang-up: the tool only SIGNALS intent (and runs optional\n * bookkeeping) — it can't reach the LiveKit room from inside `agent.stream()`. The worker owns the\n * hang-up: with `configuration.endCall` set, it watches each turn for this tool, waits for the\n * agent's closing words to finish playing, then disconnects (running `onCallEnd` on the way out).\n * Keeping the tool inside the single agent means it stays visible/editable in Studio and works on\n * the agent and workflow reply paths alike.\n *\n * ```ts\n * // agent tools\n * endCall: createEndCallTool({\n * onEndCall: ({ reason, resourceId }) => log.info('agent ended call', { reason, resourceId }),\n * }),\n * // worker\n * createLiveKitWorker({ mastra, agent: 'support', configuration: { endCall: {} } });\n * ```\n */\nexport function createEndCallTool(options: EndCallToolOptions = {}) {\n return createTool({\n id: options.id ?? 'endCall',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n reason: z\n .string()\n .nullish()\n .describe('Short reason the call is ending, e.g. \"caller said goodbye\" or \"task complete\".'),\n }),\n outputSchema: z.object({ ended: z.boolean() }),\n execute: async ({ reason }, { agent }) => {\n try {\n await options.onEndCall?.({\n reason: reason ?? undefined,\n resourceId: agent?.resourceId,\n threadId: agent?.threadId,\n });\n } catch (error) {\n // Bookkeeping is best-effort — the tool must still signal `{ ended: true }` so the worker's\n // end-call detector runs and the call actually hangs up.\n console.warn('@mastra/livekit: onEndCall hook threw', error);\n }\n return { ended: true as const };\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["DEFAULT_DESCRIPTION","z","z"],"sources":["../src/recording.ts","../src/dispatch.ts","../src/routes.ts","../src/recording-route.ts","../src/consent.ts","../src/end-call.ts"],"sourcesContent":["import { RoomEgress, RoomServiceClient } from 'livekit-server-sdk';\n\n/**\n * LiveKit auto-egress settings, accepted as a plain object or a RoomEgress instance.\n * Use `room.audioOnly` and `room.fileOutputs` for a single mixed audio recording.\n * Storage credentials belong here on the server, never in session metadata or tokens.\n */\nexport type LiveKitRecordingOptions = NonNullable<ConstructorParameters<typeof RoomEgress>[0]>;\n\n/** A recording-enabled session cannot reuse an existing LiveKit room. */\nexport class LiveKitRecordingRoomConflictError extends Error {\n readonly roomName: string;\n\n constructor(roomName: string) {\n super(\n `@mastra/livekit: recording requires a new room, but room \"${roomName}\" already exists. Use a fresh room name.`,\n );\n this.name = 'LiveKitRecordingRoomConflictError';\n this.roomName = roomName;\n }\n}\n\n/** Creates a fresh room with recording configured before any agent or caller joins. */\nexport async function createRecordingRoom(options: {\n roomName: string;\n serverUrl: string;\n apiKey: string;\n apiSecret: string;\n recording: LiveKitRecordingOptions;\n}) {\n const { recording, roomName } = options;\n if (!recording.room && !recording.participant && !recording.tracks) {\n throw new Error('@mastra/livekit: recording must configure at least one of room, participant, or tracks.');\n }\n\n const client = new RoomServiceClient(options.serverUrl.replace(/^ws/, 'http'), options.apiKey, options.apiSecret);\n // CreateRoom reuses existing rooms without applying room-composite auto-egress.\n // This preflight is not atomic: callers must use a fresh name and serialize room creation.\n const rooms = await client.listRooms([roomName]);\n if (rooms.length > 0) {\n throw new LiveKitRecordingRoomConflictError(roomName);\n }\n\n return client.createRoom({ name: roomName, egress: new RoomEgress(recording) });\n}\n","import { AgentDispatchClient } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { createRecordingRoom } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\nexport interface DispatchVoiceSessionOptions {\n /**\n * Room to dispatch the agent into (created on demand). With recording enabled, use a unique\n * name for each call. An existing room throws LiveKitRecordingRoomConflictError.\n */\n roomName: string;\n /** Must match the worker's `agentName`. Defaults to `'mastra-voice'`. */\n agentName?: string;\n metadata?: LiveKitSessionMetadata;\n /** Create a fresh room with automatic recording before dispatch. Omit to keep dispatch-only behavior. */\n recording?: LiveKitRecordingOptions;\n /** Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n}\n\nfunction toHttpUrl(url: string): string {\n return url.replace(/^ws/, 'http');\n}\n\n/**\n * Programmatically dispatches a Mastra voice agent into a LiveKit room — for\n * server-initiated sessions such as outbound calls or joining an existing room.\n */\nexport async function dispatchVoiceSession(options: DispatchVoiceSessionOptions) {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl) {\n throw new Error('@mastra/livekit: set LIVEKIT_URL or pass serverUrl to dispatchVoiceSession.');\n }\n if (!apiKey || !apiSecret) {\n throw new Error(\n '@mastra/livekit: set LIVEKIT_API_KEY and LIVEKIT_API_SECRET or pass apiKey/apiSecret to dispatchVoiceSession.',\n );\n }\n const metadata = serializeSessionMetadata(options.metadata ?? {});\n if (options.recording !== undefined) {\n await createRecordingRoom({\n roomName: options.roomName,\n serverUrl,\n apiKey,\n apiSecret,\n recording: options.recording,\n });\n }\n const client = new AgentDispatchClient(toHttpUrl(serverUrl), apiKey, apiSecret);\n return client.createDispatch(options.roomName, options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME, {\n metadata,\n });\n}\n","import { RoomAgentDispatch, RoomConfiguration } from '@livekit/protocol';\nimport type { ContextWithMastra, ApiRoute } from '@mastra/core/server';\nimport { AccessToken } from 'livekit-server-sdk';\nimport { DEFAULT_LIVEKIT_AGENT_NAME } from './constants';\nimport { dispatchVoiceSession } from './dispatch';\nimport { serializeSessionMetadata } from './metadata';\nimport type { LiveKitSessionMetadata } from './metadata';\nimport { LiveKitRecordingRoomConflictError } from './recording';\nimport type { LiveKitRecordingOptions } from './recording';\n\n/** Response body of the connection-details route. Matches LiveKit's frontend starter contract. */\nexport interface LiveKitConnectionDetails {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n}\n\nexport interface ConnectionRequestArgs {\n body: Record<string, unknown>;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitConnectionRouteOptions {\n /**\n * Route path. Defaults to `/voice/livekit/connection-details`. Mastra reserves the\n * `/api` prefix for built-in routes, so custom paths must not start with `/api`.\n */\n path?: string;\n /** LiveKit server URL (`wss://...`). Defaults to `LIVEKIT_URL`. */\n serverUrl?: string;\n /** Defaults to `LIVEKIT_API_KEY`. */\n apiKey?: string;\n /** Defaults to `LIVEKIT_API_SECRET`. */\n apiSecret?: string;\n /** LiveKit agent name for explicit dispatch. Must match the worker's `agentName`. */\n agentName?: string;\n /** Token time-to-live. Defaults to `'15m'`. */\n ttl?: string | number;\n /** Defaults to `true` (Mastra custom routes require auth unless opted out). */\n requiresAuth?: boolean;\n /**\n * Room name or a function that derives one from the request. Defaults to a generated name.\n * With recording enabled, use a unique name for each call. An existing room returns HTTP 409\n * before agent dispatch or token issuance.\n */\n roomName?: string | ((args: ConnectionRequestArgs) => string);\n participantIdentity?: string | ((args: ConnectionRequestArgs) => string);\n /**\n * Session metadata delivered to the worker via agent dispatch. Defaults to passing\n * through `agentId`, `threadId`, and `resourceId` from the request body.\n */\n metadata?: (args: ConnectionRequestArgs) => LiveKitSessionMetadata | Promise<LiveKitSessionMetadata>;\n /**\n * Create a fresh room with automatic recording and dispatch the agent before returning a token.\n * The server-side callback receives the resolved room name; return undefined to skip recording.\n * Recording settings and storage credentials are never included in the participant token.\n */\n recording?:\n | LiveKitRecordingOptions\n | ((\n args: ConnectionRequestArgs & { roomName: string },\n ) => LiveKitRecordingOptions | undefined | Promise<LiveKitRecordingOptions | undefined>);\n}\n\nfunction stringField(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * An ApiRoute for `server.apiRoutes` that mints a LiveKit access token with the Mastra\n * voice agent dispatched into the room. Frontends call it to join a voice session:\n *\n * ```ts\n * export const mastra = new Mastra({\n * agents: { support },\n * server: { apiRoutes: [liveKitConnectionRoute({ agentName: 'mastra-voice' })] },\n * });\n * ```\n */\nexport function liveKitConnectionRoute(options: LiveKitConnectionRouteOptions = {}): ApiRoute {\n const handler = async (c: ContextWithMastra) => {\n const serverUrl = options.serverUrl ?? process.env.LIVEKIT_URL;\n const apiKey = options.apiKey ?? process.env.LIVEKIT_API_KEY;\n const apiSecret = options.apiSecret ?? process.env.LIVEKIT_API_SECRET;\n if (!serverUrl || !apiKey || !apiSecret) {\n return c.json(\n {\n error:\n 'LiveKit is not configured. Set LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET ' +\n '(or pass serverUrl/apiKey/apiSecret to liveKitConnectionRoute).',\n },\n 500,\n );\n }\n\n const body: Record<string, unknown> = await c.req\n .json()\n .then((parsed: unknown) =>\n parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {},\n )\n .catch(() => ({}));\n const args: ConnectionRequestArgs = { body, context: c };\n\n const metadata: LiveKitSessionMetadata = options.metadata\n ? await options.metadata(args)\n : {\n agentId: stringField(body, 'agentId'),\n threadId: stringField(body, 'threadId'),\n resourceId: stringField(body, 'resourceId'),\n };\n\n const roomName =\n typeof options.roomName === 'function'\n ? options.roomName(args)\n : (options.roomName ?? `mastra-voice-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n const identity =\n typeof options.participantIdentity === 'function'\n ? options.participantIdentity(args)\n : (options.participantIdentity ?? metadata.resourceId ?? `user-${globalThis.crypto.randomUUID().slice(0, 8)}`);\n // One memory thread per room unless the caller pins a thread explicitly.\n metadata.threadId ??= roomName;\n\n const recording =\n typeof options.recording === 'function' ? await options.recording({ ...args, roomName }) : options.recording;\n if (recording !== undefined) {\n try {\n await dispatchVoiceSession({\n roomName,\n agentName: options.agentName,\n metadata,\n serverUrl,\n apiKey,\n apiSecret,\n recording,\n });\n } catch (error) {\n if (error instanceof LiveKitRecordingRoomConflictError) {\n return c.json({ error: 'Recording requires a new room. Use a unique roomName for each call.' }, 409);\n }\n throw error;\n }\n }\n\n const token = new AccessToken(apiKey, apiSecret, { identity, ttl: options.ttl ?? '15m' });\n token.addGrant({\n room: roomName,\n roomJoin: true,\n canPublish: true,\n canSubscribe: true,\n canPublishData: true,\n canUpdateOwnMetadata: true,\n });\n // A pre-created room needs explicit dispatch; token roomConfig only applies at room creation.\n if (recording === undefined) {\n token.roomConfig = new RoomConfiguration({\n agents: [\n new RoomAgentDispatch({\n agentName: options.agentName ?? DEFAULT_LIVEKIT_AGENT_NAME,\n metadata: serializeSessionMetadata(metadata),\n }),\n ],\n });\n }\n\n const details: LiveKitConnectionDetails = {\n serverUrl,\n roomName,\n participantName: identity,\n participantToken: await token.toJwt(),\n };\n return c.json(details);\n };\n\n return {\n // Studio calls this default path and /api/system/packages reports it — change all three together.\n path: options.path ?? '/voice/livekit/connection-details',\n method: 'POST',\n requiresAuth: options.requiresAuth,\n handler,\n } as ApiRoute;\n}\n","import type { ApiRoute, ContextWithMastra } from '@mastra/core/server';\nimport type { SpanRecord } from '@mastra/core/storage';\nimport { z } from 'zod/v4';\nimport type { LiveKitRecording, LiveKitRecordingResponse } from './recording-types';\n\nconst traceIdSchema = z.string().min(1).max(256);\nexport const recordingSchema = z.object({\n url: z.url({ protocol: /^https?$/ }),\n expiresAt: z.string().datetime().optional(),\n}) satisfies z.ZodType<LiveKitRecording>;\n\nexport interface LiveKitRecordingResolverArgs {\n traceId: string;\n /** Taken from the stored voice call span, never from the browser request. */\n roomName: string;\n span: SpanRecord;\n context: ContextWithMastra;\n}\n\nexport interface LiveKitRecordingRouteOptions {\n /** Studio discovers this route at its default path: /voice/livekit/recordings/:traceId. */\n path?: string;\n /** Defaults to true. Only disable for a local demo. */\n requiresAuth?: boolean;\n /** Required trace-specific access policy. Only true permits trace lookup and URL resolution. */\n authorize: (args: { traceId: string; context: ContextWithMastra }) => boolean | Promise<boolean>;\n /** Return a playable URL, or undefined if the recording is not available yet. */\n resolveRecording: (args: LiveKitRecordingResolverArgs) => Promise<LiveKitRecording | undefined>;\n}\n\n/** Resolves a voice call trace to a recording without persisting signed URLs in traces. */\nexport function liveKitRecordingRoute(options: LiveKitRecordingRouteOptions): ApiRoute {\n if (typeof options.authorize !== 'function') {\n throw new Error('liveKitRecordingRoute requires an authorize callback.');\n }\n return {\n path: options.path ?? '/voice/livekit/recordings/:traceId',\n method: 'GET',\n requiresAuth: options.requiresAuth ?? true,\n handler: async (context: ContextWithMastra) => {\n context.header('Cache-Control', 'no-store');\n const input = traceIdSchema.safeParse(context.req.param('traceId'));\n if (!input.success) return context.json({ error: 'A valid trace ID is required.' }, 400);\n const traceId = input.data;\n try {\n if ((await options.authorize({ traceId, context })) !== true) {\n return context.json({ error: 'Access to this recording is denied.' }, 403);\n }\n const store = await context.get('mastra').getStorage()?.getStore('observability');\n if (!store) return context.json({ error: 'Trace storage is not configured.' }, 503);\n const trace = await store.getTrace({ traceId });\n const calls =\n trace?.spans.filter(\n span =>\n span.name === 'voice call' &&\n typeof span.metadata?.roomName === 'string' &&\n span.metadata.roomName.length > 0,\n ) ?? [];\n if (!calls.length) return context.json({ error: 'Voice call trace not found.' }, 404);\n if (calls.length !== 1) return context.json({ error: 'This trace contains multiple voice calls.' }, 409);\n const span = calls[0]!;\n const roomName = span.metadata?.roomName;\n if (typeof roomName !== 'string') return context.json({ error: 'Voice call trace not found.' }, 404);\n const recording = await options.resolveRecording({ traceId, roomName, span, context });\n if (!recording) return context.json({ status: 'unavailable' } satisfies LiveKitRecordingResponse);\n // Validate and strip extra fields so storage credentials cannot accidentally become response fields.\n const result = recordingSchema.safeParse(recording);\n if (!result.success) return context.json({ error: 'Unable to load the call recording.' }, 502);\n return context.json({ status: 'ready', ...result.data } satisfies LiveKitRecordingResponse);\n } catch {\n // Storage/provider errors can contain credentials or signed request details.\n return context.json({ error: 'Unable to load the call recording.' }, 502);\n }\n },\n };\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** A consent decision captured from the caller during a call. */\nexport interface ConsentGrant {\n /**\n * The consent item, matching a key in the worker's `configuration.consentPolicy` (e.g.\n * `'summaryStorage'`).\n */\n item: string;\n /** `true` if the caller agreed, `false` if they declined. */\n granted: boolean;\n /** The caller the grant belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread the grant was captured on, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface ConsentToolOptions {\n /**\n * Persist the caller's decision to YOUR system of record (database, CRM, consent ledger). Called\n * each time the caller answers a consent question — keep it quick, it runs inside the turn. This\n * tool owns extracting the caller's identity from the tool execution context; you own storage, so\n * consent lives in a durable, compliant place rather than an opaque plugin store. Read those\n * grants back at `onCallEnd` (or before any consent-gated step) to enforce the requirement.\n */\n onGrant: (grant: ConsentGrant) => void | Promise<void>;\n /** Tool id the agent calls. Defaults to `'recordConsent'`. */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /** Restrict the accepted consent items to this set (becomes an enum). Defaults to any string. */\n items?: readonly string[];\n}\n\nconst DEFAULT_DESCRIPTION =\n \"Record the caller's consent decision for a data-processing item (for example, storing a summary \" +\n 'of the call). Call this as soon as the caller answers a consent question: granted = true if they ' +\n 'agree, false if they decline. Do not proceed with an activity that requires consent until you ' +\n 'have recorded the caller granting it.';\n\n/**\n * Builds a Mastra tool that captures the caller's consent decisions at runtime — the companion to\n * the worker's `configuration.consentPolicy`, which only DECLARES which consents a call needs. Add\n * it to the agent that answers the call; on each call the tool reads the caller's `resourceId` /\n * `threadId` from its execution context and hands the decision to your `onGrant` store. The runtime\n * grants live only where `onGrant` put them: enforce consent by reading them back from your own\n * store — at `onCallEnd` or before any consent-gated action — cross-checking against the declared\n * requirements in `VoiceCallEndArgs.configuration.consentPolicy` (the configuration carries the\n * policy, never the grants).\n *\n * ```ts\n * // agent tools\n * recordConsent: createConsentTool({\n * items: ['summaryStorage'],\n * onGrant: async ({ item, granted, resourceId }) => {\n * if (resourceId) await db.saveConsent(resourceId, item, granted);\n * },\n * }),\n * ```\n */\nexport function createConsentTool(options: ConsentToolOptions) {\n const items = options.items;\n const itemSchema = items && items.length > 0 ? z.enum([...items] as [string, ...string[]]) : z.string();\n return createTool({\n id: options.id ?? 'recordConsent',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n item: itemSchema.describe(\n \"The consent item the caller is answering for (matches the deployment's required consents).\",\n ),\n granted: z.boolean().describe('True if the caller agreed, false if they declined.'),\n }),\n execute: async ({ item, granted }, { agent }) => {\n await options.onGrant({ item, granted, resourceId: agent?.resourceId, threadId: agent?.threadId });\n return { recorded: true as const, item, granted };\n },\n });\n}\n","// Server-safe: only depends on `@mastra/core` + zod, never the `@livekit/agents` runtime — so it\n// can be added to agents defined in server/shared code and re-exported from the root entry\n// (`index.ts`). `index.test.ts` enforces that boundary.\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\n/** Details of the agent's decision to end the call, handed to {@link EndCallToolOptions.onEndCall}. */\nexport interface EndCallRequest {\n /** Short reason the agent gave for ending the call, if any (e.g. \"caller said goodbye\"). */\n reason?: string;\n /** The caller the call belongs to (the memory `resource`), when the call is memory-scoped. */\n resourceId?: string;\n /** The call thread, when the call is memory-scoped. */\n threadId?: string;\n}\n\nexport interface EndCallToolOptions {\n /**\n * Tool id the agent calls to end the call. Must match the worker's\n * `configuration.endCall.tool` (both default to `'endCall'`), since that's the name the worker\n * watches for to hang up.\n */\n id?: string;\n /** Override the description the model sees when deciding to call the tool. */\n description?: string;\n /**\n * Optional bookkeeping hook, called when the agent invokes the tool (records the reason, marks\n * the call resolved in your CRM, …). Runs inside the turn — keep it quick. It does NOT hang up\n * the call; the worker does that once the agent's final words finish playing. The tool reads the\n * caller's identity from its execution context and passes it here.\n */\n onEndCall?: (request: EndCallRequest) => void | Promise<void>;\n}\n\nconst DEFAULT_DESCRIPTION =\n 'End the phone call. Call this only after you have said goodbye and there is nothing left to do — ' +\n 'for example the caller says goodbye, or the task is complete and you have wrapped up. Say your ' +\n 'closing line first, then call this as your final action; the call hangs up once your words finish ' +\n 'playing. Your closing line must be a final statement, never a question — if you are asking the ' +\n 'caller anything (for example whether they need anything else), do NOT call this in that reply; ' +\n 'wait for their answer. Do not call it if the caller still needs something.';\n\n/**\n * Builds a Mastra tool the agent calls to end the call itself (say goodbye → hang up). It is the\n * agent-visible half of agent-initiated hang-up: the tool only SIGNALS intent (and runs optional\n * bookkeeping) — it can't reach the LiveKit room from inside `agent.stream()`. The worker owns the\n * hang-up: with `configuration.endCall` set, it watches each turn for this tool, waits for the\n * agent's closing words to finish playing, then disconnects (running `onCallEnd` on the way out).\n * Keeping the tool inside the single agent means it stays visible/editable in Studio and works on\n * the agent and workflow reply paths alike.\n *\n * ```ts\n * // agent tools\n * endCall: createEndCallTool({\n * onEndCall: ({ reason, resourceId }) => log.info('agent ended call', { reason, resourceId }),\n * }),\n * // worker\n * createLiveKitWorker({ mastra, agent: 'support', configuration: { endCall: {} } });\n * ```\n */\nexport function createEndCallTool(options: EndCallToolOptions = {}) {\n return createTool({\n id: options.id ?? 'endCall',\n description: options.description ?? DEFAULT_DESCRIPTION,\n inputSchema: z.object({\n reason: z\n .string()\n .nullish()\n .describe('Short reason the call is ending, e.g. \"caller said goodbye\" or \"task complete\".'),\n }),\n outputSchema: z.object({ ended: z.boolean() }),\n execute: async ({ reason }, { agent }) => {\n try {\n await options.onEndCall?.({\n reason: reason ?? undefined,\n resourceId: agent?.resourceId,\n threadId: agent?.threadId,\n });\n } catch (error) {\n // Bookkeeping is best-effort — the tool must still signal `{ ended: true }` so the worker's\n // end-call detector runs and the call actually hangs up.\n console.warn('@mastra/livekit: onEndCall hook threw', error);\n }\n return { ended: true as const };\n },\n });\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_turn_metrics = require("./turn-metrics-CtVHHdJj.cjs");
3
- const require_remote = require("./remote-z8wjPDtk.cjs");
2
+ const require_turn_metrics = require("./turn-metrics-Dd8pYEbh.cjs");
3
+ const require_remote = require("./remote-DO33RaMO.cjs");
4
4
  let _livekit_agents = require("@livekit/agents");
5
5
  let _mastra_core_request_context = require("@mastra/core/request-context");
6
- //#region src/llm-plugin.ts
6
+ // src/llm-plugin.ts
7
7
  function toRequestContext(value) {
8
8
  if (!value) return void 0;
9
9
  if (value instanceof _mastra_core_request_context.RequestContext) return value;
@@ -212,7 +212,6 @@ var MastraLLMStream = class extends _livekit_agents.llm.LLMStream {
212
212
  });
213
213
  }
214
214
  };
215
- //#endregion
216
215
  exports.MastraLLM = MastraLLM;
217
216
  exports.VOICE_TEXT_FLUSH = require_turn_metrics.VOICE_TEXT_FLUSH;
218
217
  exports.createRemoteAgentReplyGenerator = require_remote.createRemoteAgentReplyGenerator;
@@ -1,8 +1,8 @@
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-Dgn5fQLl.js";
1
+ import { n as VOICE_TEXT_FLUSH, r as VOICE_TURN_METADATA, t as VOICE_FLUSH_METADATA } from "./turn-metrics-K8_ttSW7.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-DNmutTEU.js";
3
3
  import { DEFAULT_API_CONNECT_OPTIONS, llm } from "@livekit/agents";
4
4
  import { RequestContext } from "@mastra/core/request-context";
5
- //#region src/llm-plugin.ts
5
+ // src/llm-plugin.ts
6
6
  function toRequestContext(value) {
7
7
  if (!value) return void 0;
8
8
  if (value instanceof RequestContext) return value;
@@ -211,7 +211,6 @@ var MastraLLMStream = class extends llm.LLMStream {
211
211
  });
212
212
  }
213
213
  };
214
- //#endregion
215
214
  export { MastraLLM, VOICE_TEXT_FLUSH, createRemoteAgentReplyGenerator, mastraLLMNode, observeVoiceSession };
216
215
 
217
216
  //# sourceMappingURL=plugin-entry.js.map
@@ -1,4 +1,4 @@
1
- import { a as notifyVoiceObserver, i as createGenerationMetrics, n as VOICE_TEXT_FLUSH, r as VOICE_TURN_METADATA } from "./turn-metrics-CqqzdLDe.js";
1
+ import { a as notifyVoiceObserver, i as createGenerationMetrics, n as VOICE_TEXT_FLUSH, r as VOICE_TURN_METADATA } from "./turn-metrics-K8_ttSW7.js";
2
2
  import { ReadableStream as ReadableStream$1 } from "stream/web";
3
3
  import { APIConnectionError, APIError, APIStatusError, APITimeoutError, FlushSentinel, llm, voice } from "@livekit/agents";
4
4
  import { RequestContext } from "@mastra/core/request-context";
@@ -84,8 +84,7 @@ function chatContextToMessages(chatCtx) {
84
84
  }
85
85
  return messages;
86
86
  }
87
- //#endregion
88
- //#region src/voice-stream.ts
87
+ // src/voice-stream.ts
89
88
  /** A one-to-one mapping that awaits upstream cancellation, including pending reads. */
90
89
  function mapVoiceStream(source, map) {
91
90
  const reader = source.getReader();
@@ -107,8 +106,7 @@ function mapVoiceStream(source, map) {
107
106
  }
108
107
  });
109
108
  }
110
- //#endregion
111
- //#region src/bridge.ts
109
+ // src/bridge.ts
112
110
  const DEFAULT_INSTRUCTIONS = "You are a helpful voice assistant powered by a Mastra agent.";
113
111
  /**
114
112
  * Tracks periodic AI re-disclosure for a single call. `due()` returns the reminder text once
@@ -389,7 +387,7 @@ var MastraVoiceAgent = class extends voice.Agent {
389
387
  extra: { [VOICE_TURN_METADATA]: identity }
390
388
  }
391
389
  };
392
- else return FlushSentinel;
390
+ return FlushSentinel;
393
391
  });
394
392
  }
395
393
  };
@@ -451,8 +449,7 @@ async function instrumentVoiceReply(generator, ctx, onTurnMetrics) {
451
449
  })
452
450
  };
453
451
  }
454
- //#endregion
455
- //#region src/speech.ts
452
+ // src/speech.ts
456
453
  /**
457
454
  * Use from a custom Agent.llmNode when using MastraLLM. LiveKit's LLMStream only accepts
458
455
  * ChatChunks; this node translates our boundary metadata into LiveKit's supported marker.
@@ -462,7 +459,7 @@ async function mastraLLMNode(agent, chatCtx, toolCtx, modelSettings) {
462
459
  if (!source) return null;
463
460
  return mapVoiceStream(source, (chunk) => {
464
461
  if (typeof chunk === "object" && chunk.delta?.extra?.["mastra.voice.flush"] === true) return FlushSentinel;
465
- else return chunk;
462
+ return chunk;
466
463
  });
467
464
  }
468
465
  function speechResult(handle, outcome) {
@@ -527,8 +524,7 @@ function observeVoiceSession(session, options) {
527
524
  session.on(voice.AgentSessionEventTypes.Close, onClose);
528
525
  return () => detach("cancelled");
529
526
  }
530
- //#endregion
531
- //#region src/remote.ts
527
+ // src/remote.ts
532
528
  const DEFAULT_API_PREFIX = "/api";
533
529
  /** Connect + first-token budget when not overridden. Plugin mode passes `connOptions.timeoutMs`. */
534
530
  const DEFAULT_REMOTE_TIMEOUT_MS = 1e4;
@@ -815,7 +811,6 @@ function createRemoteAgentReplyGenerator(options) {
815
811
  });
816
812
  };
817
813
  }
818
- //#endregion
819
814
  export { createAgentReplyGenerator as a, chatContextToMessages as c, MastraVoiceAgent as i, extractNewTurnMessages as l, mastraLLMNode as n, createMastraVoiceAgent as o, observeVoiceSession as r, instrumentVoiceReply as s, createRemoteAgentReplyGenerator as t };
820
815
 
821
- //# sourceMappingURL=remote-Dgn5fQLl.js.map
816
+ //# sourceMappingURL=remote-DNmutTEU.js.map