@mastra/connect 1.0.4-alpha.0 → 1.1.0-alpha.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { tools } from './tools.js';
2
- export type { ToolsOptions, ToolsProviderOptions, ToolsResolver, ToolsResolverContext, ToolsWithInput, } from './tools.js';
2
+ export type { ToolsOptions, ToolsOptionsInput, ToolsProviderOptions, ToolsResolver, ToolsResolverContext, ToolsWithInput, } from './tools.js';
3
3
  export { channels } from './channels.js';
4
4
  export type { ChannelsOptions, ChannelsProviderOptions, ChannelsResolver, ChannelsResolverContext, ResolvedChannels, } from './channels.js';
5
5
  export type { ChannelProviderRegistration, ChannelInstance, ChannelRuntime } from './providers/channel-provider.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,2BAA2B,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACpH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,2BAA2B,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACpH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import { MCPClient } from "@mastra/mcp";
2
2
  import { z } from "zod";
3
3
  import { SpanType, resolveCurrentSpan } from "@mastra/core/observability";
4
4
  import { createTool } from "@mastra/core/tools";
5
+ import { hkdfSync } from "crypto";
5
6
  //#region src/errors.ts
6
7
  const MAX_DETAIL_LENGTH = 2e3;
7
8
  var MastraConnectError = class extends Error {
@@ -244,6 +245,8 @@ function resolveClient(options) {
244
245
  };
245
246
  const orgId = options?.orgId?.trim() || process.env.MASTRA_ORG_ID?.trim();
246
247
  if (orgId) headers["x-organization-id"] = orgId;
248
+ const projectId = options?.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
249
+ if (projectId) headers["x-mastra-project-id"] = projectId;
247
250
  return {
248
251
  baseUrl,
249
252
  headers,
@@ -1026,7 +1029,6 @@ const CHANNELS$1 = [
1026
1029
  integrationId: "microsoft-teams",
1027
1030
  async create(options, runtime) {
1028
1031
  const mod = await import("@mastra/teams");
1029
- if (!process.env.MASTRA_ENCRYPTION_KEY) throw new MastraConnectError("invalid_options", "Microsoft Teams channel: MASTRA_ENCRYPTION_KEY is not set. The Teams install store persists per-agent bot secrets and requires an at-rest encryption key (a 32-byte value, base64-encoded).");
1030
1032
  const safeOptions = stripReservedOptions("microsoft-teams", options);
1031
1033
  const tokenResolver = async (scope) => {
1032
1034
  const fresh = await runtime.getCredential();
@@ -1035,10 +1037,22 @@ const CHANNELS$1 = [
1035
1037
  if (!token) throw new MastraConnectError("no_active_connection", `Teams connection ${runtime.getConnectionId()} has no Dev Portal token on its credential. Reconnect with an integration that requests the ${mod.TEAMS_DEV_PORTAL_SCOPE} scope.`);
1036
1038
  return token;
1037
1039
  };
1038
- return { provider: new mod.TeamsProvider({
1040
+ const config = {
1039
1041
  tokenResolver,
1040
1042
  ...safeOptions ?? {}
1041
- }) };
1043
+ };
1044
+ const usingDerivedKey = !process.env.MASTRA_ENCRYPTION_KEY;
1045
+ if (usingDerivedKey) config.encryptionKey = Buffer.from(hkdfSync("sha256", runtime.client.accessToken, "mastra-connect", "microsoft-teams-install-store", 32)).toString("base64");
1046
+ let warnedDerivedKey = false;
1047
+ return {
1048
+ provider: new mod.TeamsProvider(config),
1049
+ async sync() {
1050
+ if (usingDerivedKey && !warnedDerivedKey) {
1051
+ warnedDerivedKey = true;
1052
+ console.warn("[@mastra/connect] MASTRA_ENCRYPTION_KEY is not set: encrypting the Microsoft Teams install store with a key derived from the platform access token. Set MASTRA_ENCRYPTION_KEY to use a dedicated key — otherwise rotating the platform access token makes stored bot credentials unreadable (disconnect and reconnect the channel to re-provision).");
1053
+ }
1054
+ }
1055
+ };
1042
1056
  }
1043
1057
  }
1044
1058
  ];
@@ -7873,10 +7887,15 @@ const SentenceSchema = z.object({
7873
7887
  speaker_name: z.string().nullish(),
7874
7888
  speaker_id: z.string().nullish(),
7875
7889
  raw_text: z.string().nullish(),
7876
- start_time: z.string().nullish(),
7877
- end_time: z.string().nullish(),
7890
+ start_time: z.number().nullish(),
7891
+ end_time: z.number().nullish(),
7878
7892
  text: z.string().nullish()
7879
7893
  });
7894
+ const ProviderSentenceSchema = SentenceSchema.extend({
7895
+ speaker_id: z.union([z.string(), z.number()]).nullish(),
7896
+ start_time: z.union([z.number(), z.string()]).nullish(),
7897
+ end_time: z.union([z.number(), z.string()]).nullish()
7898
+ });
7880
7899
  const SummarySectionSchema = z.object({
7881
7900
  title: z.string().nullish(),
7882
7901
  content: z.string().nullish()
@@ -7943,7 +7962,7 @@ const AnalyticsSchema = z.object({
7943
7962
  const TranscriptSchema = z.object({
7944
7963
  id: z.string().nullish(),
7945
7964
  title: z.string().nullish(),
7946
- sentences: z.array(SentenceSchema).nullish(),
7965
+ sentences: z.array(ProviderSentenceSchema).nullish(),
7947
7966
  summary: ProviderSummarySchema$1.nullable().optional(),
7948
7967
  speakers: z.array(SpeakerSchema).nullish(),
7949
7968
  meeting_info: MeetingInfoSchema.nullable().optional(),
@@ -7971,6 +7990,22 @@ const getTranscriptOutputSchema = z.object({
7971
7990
  function normalizeActionItems$1(actionItems) {
7972
7991
  return Array.isArray(actionItems) ? actionItems.join("\n") : actionItems;
7973
7992
  }
7993
+ function toFiniteNumber(value) {
7994
+ if (value == null) return void 0;
7995
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
7996
+ const trimmed = value.trim();
7997
+ if (trimmed === "") return void 0;
7998
+ const parsed = Number(trimmed);
7999
+ return Number.isFinite(parsed) ? parsed : void 0;
8000
+ }
8001
+ function normalizeSentence(sentence) {
8002
+ return {
8003
+ ...sentence,
8004
+ speaker_id: sentence.speaker_id != null ? String(sentence.speaker_id) : sentence.speaker_id,
8005
+ start_time: toFiniteNumber(sentence.start_time),
8006
+ end_time: toFiniteNumber(sentence.end_time)
8007
+ };
8008
+ }
7974
8009
  function getTranscriptTool(proxy) {
7975
8010
  return createTool({
7976
8011
  id: "fireflies_get_transcript",
@@ -8076,7 +8111,7 @@ function getTranscriptTool(proxy) {
8076
8111
  return {
8077
8112
  id: transcript.id ?? void 0,
8078
8113
  title: transcript.title ?? void 0,
8079
- sentences: transcript.sentences ?? void 0,
8114
+ sentences: transcript.sentences ? transcript.sentences.map(normalizeSentence) : void 0,
8080
8115
  summary: transcript.summary ? {
8081
8116
  ...transcript.summary,
8082
8117
  action_items: transcript.summary.action_items != null ? normalizeActionItems$1(transcript.summary.action_items) : transcript.summary.action_items
@@ -19805,11 +19840,11 @@ function insertTextTool(proxy) {
19805
19840
  }
19806
19841
  //#endregion
19807
19842
  //#region src/providers/google-docs/tools/list-revisions.ts
19808
- const listRevisionsInputSchema = z.object({
19843
+ const listRevisionsInputSchema$1 = z.object({
19809
19844
  documentId: z.string().describe("Google Docs document ID. Example: \"1Kj3d86Z-Sfd56YP4dImQ-ggMRyP2QZ_BRO33zOO224c\""),
19810
19845
  pageToken: z.string().optional().describe("Pagination token from the previous response. Omit for the first page.")
19811
19846
  });
19812
- const LastModifyingUserSchema = z.object({
19847
+ const LastModifyingUserSchema$1 = z.object({
19813
19848
  kind: z.string().optional(),
19814
19849
  displayName: z.string().optional(),
19815
19850
  photoLink: z.string().optional(),
@@ -19817,27 +19852,27 @@ const LastModifyingUserSchema = z.object({
19817
19852
  permissionId: z.string().optional(),
19818
19853
  emailAddress: z.string().optional()
19819
19854
  });
19820
- const RevisionSchema = z.object({
19855
+ const RevisionSchema$1 = z.object({
19821
19856
  id: z.string(),
19822
19857
  modifiedTime: z.string().optional(),
19823
- lastModifyingUser: LastModifyingUserSchema.optional(),
19858
+ lastModifyingUser: LastModifyingUserSchema$1.optional(),
19824
19859
  keepForever: z.boolean().optional(),
19825
19860
  published: z.boolean().optional()
19826
19861
  });
19827
- const listRevisionsOutputSchema = z.object({
19828
- revisions: z.array(RevisionSchema),
19862
+ const listRevisionsOutputSchema$1 = z.object({
19863
+ revisions: z.array(RevisionSchema$1),
19829
19864
  nextPageToken: z.string().optional()
19830
19865
  });
19831
- const DriveRevisionsResponseSchema = z.object({
19866
+ const DriveRevisionsResponseSchema$1 = z.object({
19832
19867
  nextPageToken: z.string().optional(),
19833
19868
  revisions: z.array(z.unknown()).optional()
19834
19869
  });
19835
- function listRevisionsTool(proxy) {
19870
+ function listRevisionsTool$1(proxy) {
19836
19871
  return createTool({
19837
19872
  id: "google_docs_list_revisions",
19838
19873
  description: "List the revision history for a Google Doc.",
19839
- inputSchema: listRevisionsInputSchema,
19840
- outputSchema: listRevisionsOutputSchema,
19874
+ inputSchema: listRevisionsInputSchema$1,
19875
+ outputSchema: listRevisionsOutputSchema$1,
19841
19876
  execute: async (input, { requestContext }) => {
19842
19877
  const platformProxy = proxy.withRequestContext(requestContext);
19843
19878
  const response = await platformProxy.get({
@@ -19850,7 +19885,7 @@ function listRevisionsTool(proxy) {
19850
19885
  baseUrlOverride: "https://www.googleapis.com",
19851
19886
  retries: 3
19852
19887
  });
19853
- const parsedResponse = DriveRevisionsResponseSchema.safeParse(response.data);
19888
+ const parsedResponse = DriveRevisionsResponseSchema$1.safeParse(response.data);
19854
19889
  if (!parsedResponse.success) throw new platformProxy.ActionError({
19855
19890
  type: "invalid_response",
19856
19891
  message: "Invalid response from Google Drive API"
@@ -19858,7 +19893,7 @@ function listRevisionsTool(proxy) {
19858
19893
  const rawRevisions = parsedResponse.data.revisions ?? [];
19859
19894
  const revisions = [];
19860
19895
  for (const rawRevision of rawRevisions) {
19861
- const parsedRevision = RevisionSchema.safeParse(rawRevision);
19896
+ const parsedRevision = RevisionSchema$1.safeParse(rawRevision);
19862
19897
  if (parsedRevision.success) revisions.push(parsedRevision.data);
19863
19898
  }
19864
19899
  return {
@@ -20754,7 +20789,7 @@ function createGoogleDocsTools(options) {
20754
20789
  google_docs_insert_table_row: insertTableRowTool$1(platformProxy),
20755
20790
  google_docs_insert_table: insertTableTool(platformProxy),
20756
20791
  google_docs_insert_text: insertTextTool(platformProxy),
20757
- google_docs_list_revisions: listRevisionsTool(platformProxy),
20792
+ google_docs_list_revisions: listRevisionsTool$1(platformProxy),
20758
20793
  google_docs_merge_table_cells: mergeTableCellsTool(platformProxy),
20759
20794
  google_docs_pin_table_header_rows: pinTableHeaderRowsTool(platformProxy),
20760
20795
  google_docs_replace_all_text: replaceAllTextTool(platformProxy),
@@ -20908,7 +20943,10 @@ function createFolderTool(proxy) {
20908
20943
  if (input.parentId) requestBody["parents"] = [input.parentId];
20909
20944
  const response = await platformProxy.post({
20910
20945
  endpoint: "/drive/v3/files",
20911
- params: { fields: "id,name,mimeType,createdTime,parents" },
20946
+ params: {
20947
+ supportsAllDrives: "true",
20948
+ fields: "id,name,mimeType,createdTime,parents"
20949
+ },
20912
20950
  data: requestBody,
20913
20951
  retries: 3
20914
20952
  });
@@ -21107,6 +21145,56 @@ function emptyTrashTool(proxy) {
21107
21145
  });
21108
21146
  }
21109
21147
  //#endregion
21148
+ //#region src/providers/google-drive/tools/export-file.ts
21149
+ const exportFileInputSchema = z.object({
21150
+ fileId: z.string().describe("The ID of the Google Workspace file to export (Doc, Sheet, Slide, etc.). Example: \"1Kj3d86Z-Sfd56YP4dImQ-ggMRyP2QZ_BRO33zOO224c\""),
21151
+ mimeType: z.enum([
21152
+ "application/pdf",
21153
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
21154
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
21155
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
21156
+ "text/html",
21157
+ "text/plain",
21158
+ "text/csv",
21159
+ "application/rtf",
21160
+ "application/epub+zip"
21161
+ ]).describe("The target MIME type for the export.")
21162
+ });
21163
+ const exportFileOutputSchema = z.object({
21164
+ fileId: z.string(),
21165
+ mimeType: z.string(),
21166
+ data: z.string().describe("The exported file encoded as a base64 string."),
21167
+ size: z.number().describe("The size of the exported data in bytes.")
21168
+ });
21169
+ function exportFileTool(proxy) {
21170
+ return createTool({
21171
+ id: "google_drive_export_file",
21172
+ description: "Export a Google Workspace file (Doc, Sheet, Slide, etc.) to the specified MIME type.",
21173
+ inputSchema: exportFileInputSchema,
21174
+ outputSchema: exportFileOutputSchema,
21175
+ execute: async (input, { requestContext }) => {
21176
+ const platformProxy = proxy.withRequestContext(requestContext);
21177
+ const rawData = (await platformProxy.get({
21178
+ endpoint: `/drive/v3/files/${encodeURIComponent(input.fileId)}/export`,
21179
+ params: { mimeType: input.mimeType },
21180
+ responseType: "arraybuffer",
21181
+ retries: 3
21182
+ })).data;
21183
+ if (typeof rawData !== "object" || rawData === null) throw new platformProxy.ActionError({
21184
+ type: "invalid_response",
21185
+ message: "Expected binary response from export API."
21186
+ });
21187
+ const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
21188
+ return {
21189
+ fileId: input.fileId,
21190
+ mimeType: input.mimeType,
21191
+ data: buffer.toString("base64"),
21192
+ size: buffer.length
21193
+ };
21194
+ }
21195
+ });
21196
+ }
21197
+ //#endregion
21110
21198
  //#region src/providers/google-drive/tools/find-file.ts
21111
21199
  const findFileInputSchema = z.object({
21112
21200
  query: z.string().optional().describe("Search query string. Uses Google Drive search query syntax. Example: \"name contains 'report'\" or \"mimeType = 'application/pdf'\". If not provided, returns all files."),
@@ -21932,6 +22020,70 @@ function listPermissionsTool(proxy) {
21932
22020
  });
21933
22021
  }
21934
22022
  //#endregion
22023
+ //#region src/providers/google-drive/tools/list-revisions.ts
22024
+ const listRevisionsInputSchema = z.object({
22025
+ fileId: z.string().describe("The Drive file ID. Example: \"1Kj3d86Z-Sfd56YP4dImQ-ggMRyP2QZ_BRO33zOO224c\""),
22026
+ pageToken: z.string().optional().describe("Pagination token from the previous response. Omit for the first page.")
22027
+ });
22028
+ const LastModifyingUserSchema = z.object({
22029
+ kind: z.string().optional(),
22030
+ displayName: z.string().optional(),
22031
+ photoLink: z.string().optional(),
22032
+ me: z.boolean().optional(),
22033
+ permissionId: z.string().optional(),
22034
+ emailAddress: z.string().optional()
22035
+ });
22036
+ const RevisionSchema = z.object({
22037
+ id: z.string(),
22038
+ modifiedTime: z.string().optional(),
22039
+ lastModifyingUser: LastModifyingUserSchema.optional(),
22040
+ keepForever: z.boolean().optional(),
22041
+ published: z.boolean().optional()
22042
+ });
22043
+ const listRevisionsOutputSchema = z.object({
22044
+ revisions: z.array(RevisionSchema),
22045
+ nextPageToken: z.string().optional()
22046
+ });
22047
+ const DriveRevisionsResponseSchema = z.object({
22048
+ nextPageToken: z.string().optional(),
22049
+ revisions: z.array(z.unknown()).optional()
22050
+ });
22051
+ function listRevisionsTool(proxy) {
22052
+ return createTool({
22053
+ id: "google_drive_list_revisions",
22054
+ description: "List the revision history for a Drive file.",
22055
+ inputSchema: listRevisionsInputSchema,
22056
+ outputSchema: listRevisionsOutputSchema,
22057
+ execute: async (input, { requestContext }) => {
22058
+ const platformProxy = proxy.withRequestContext(requestContext);
22059
+ const response = await platformProxy.get({
22060
+ endpoint: `/drive/v3/files/${encodeURIComponent(input.fileId)}/revisions`,
22061
+ params: {
22062
+ fields: "nextPageToken,revisions(id,modifiedTime,lastModifyingUser,keepForever,published)",
22063
+ pageSize: "100",
22064
+ ...input.pageToken !== void 0 && { pageToken: input.pageToken }
22065
+ },
22066
+ retries: 3
22067
+ });
22068
+ const parsedResponse = DriveRevisionsResponseSchema.safeParse(response.data);
22069
+ if (!parsedResponse.success) throw new platformProxy.ActionError({
22070
+ type: "invalid_response",
22071
+ message: "Invalid response from Google Drive API"
22072
+ });
22073
+ const rawRevisions = parsedResponse.data.revisions ?? [];
22074
+ const revisions = [];
22075
+ for (const rawRevision of rawRevisions) {
22076
+ const parsedRevision = RevisionSchema.safeParse(rawRevision);
22077
+ if (parsedRevision.success) revisions.push(parsedRevision.data);
22078
+ }
22079
+ return {
22080
+ revisions,
22081
+ ...parsedResponse.data.nextPageToken !== void 0 && { nextPageToken: parsedResponse.data.nextPageToken }
22082
+ };
22083
+ }
22084
+ });
22085
+ }
22086
+ //#endregion
21935
22087
  //#region src/providers/google-drive/tools/move-file.ts
21936
22088
  const moveFileInputSchema = z.object({
21937
22089
  fileId: z.string().describe("The ID of the file to move. Example: \"1mD3ukEAmRqo8u0RF_Cr6IJl9f_uWTYH03vesDhB5Svw\""),
@@ -21957,6 +22109,7 @@ function moveFileTool(proxy) {
21957
22109
  params: {
21958
22110
  addParents: input.toFolderId,
21959
22111
  removeParents: input.fromFolderId,
22112
+ supportsAllDrives: "true",
21960
22113
  fields: "id,name,mimeType,parents"
21961
22114
  },
21962
22115
  retries: 3
@@ -22306,7 +22459,10 @@ function uploadDocumentTool(proxy) {
22306
22459
  if (input.folderId) metadataBody["parents"] = [input.folderId];
22307
22460
  const createResponse = await platformProxy.post({
22308
22461
  endpoint: "/drive/v3/files",
22309
- params: { fields: "id,name,mimeType,webViewLink,webContentLink" },
22462
+ params: {
22463
+ supportsAllDrives: "true",
22464
+ fields: "id,name,mimeType,webViewLink,webContentLink"
22465
+ },
22310
22466
  data: metadataBody,
22311
22467
  retries: 3
22312
22468
  });
@@ -22321,6 +22477,7 @@ function uploadDocumentTool(proxy) {
22321
22477
  endpoint: `/upload/drive/v3/files/${fileId}`,
22322
22478
  params: {
22323
22479
  uploadType: "media",
22480
+ supportsAllDrives: "true",
22324
22481
  fields: "id,name,mimeType,webViewLink,webContentLink"
22325
22482
  },
22326
22483
  headers: { "Content-Type": input.mimeType },
@@ -22331,6 +22488,7 @@ function uploadDocumentTool(proxy) {
22331
22488
  const status = error?.response?.status;
22332
22489
  if (typeof status === "number" && status >= 400 && status < 500) await platformProxy.delete({
22333
22490
  endpoint: `/drive/v3/files/${fileId}`,
22491
+ params: { supportsAllDrives: "true" },
22334
22492
  retries: 3
22335
22493
  }).catch(() => void 0);
22336
22494
  throw error;
@@ -22367,6 +22525,7 @@ function createGoogleDriveTools(options) {
22367
22525
  google_drive_delete_permission: deletePermissionTool(platformProxy),
22368
22526
  google_drive_delete_shared_drive: deleteSharedDriveTool(platformProxy),
22369
22527
  google_drive_empty_trash: emptyTrashTool(platformProxy),
22528
+ google_drive_export_file: exportFileTool(platformProxy),
22370
22529
  google_drive_find_file: findFileTool(platformProxy),
22371
22530
  google_drive_find_folder: findFolderTool(platformProxy),
22372
22531
  google_drive_get_about: getAboutTool(platformProxy),
@@ -22381,6 +22540,7 @@ function createGoogleDriveTools(options) {
22381
22540
  google_drive_list_drives: listDrivesTool(platformProxy),
22382
22541
  google_drive_list_files_non_unified: listFilesNonUnifiedTool(platformProxy),
22383
22542
  google_drive_list_permissions: listPermissionsTool(platformProxy),
22543
+ google_drive_list_revisions: listRevisionsTool(platformProxy),
22384
22544
  google_drive_move_file: moveFileTool(platformProxy),
22385
22545
  google_drive_unhide_shared_drive: unhideSharedDriveTool(platformProxy),
22386
22546
  google_drive_update_comment: updateCommentTool$2(platformProxy),
@@ -75088,13 +75248,23 @@ let nextResolverId = 0;
75088
75248
  * Expected provider absence is silently skipped. Actionable per-integration
75089
75249
  * problems during resolution (needs re-auth or ambiguity) are downgraded to
75090
75250
  * warn-and-skip so one bad integration never takes down the whole toolset.
75251
+ *
75252
+ * Pass a callback instead of an options object to vary options per request
75253
+ * (see {@link ToolsOptionsInput}). With the callback form, configuration
75254
+ * validation runs at the first resolution that produces a given options
75255
+ * value — still failing loudly — instead of at call time.
75091
75256
  */
75092
75257
  function tools(options = {}) {
75258
+ if (typeof options === "function") return dynamicTools(options);
75093
75259
  const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
75094
75260
  if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
75095
75261
  if (options.ttlMs !== void 0 && (!Number.isFinite(options.ttlMs) || options.ttlMs < 0)) throw new MastraConnectError("invalid_options", `Invalid ttlMs (${options.ttlMs}): expected a finite number of milliseconds >= 0.`);
75096
75262
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS$1;
75097
- const client = resolveClient(options.client);
75263
+ const clientOptions = {
75264
+ ...options.client,
75265
+ projectId
75266
+ };
75267
+ const client = resolveClient(clientOptions);
75098
75268
  const resolverId = ++nextResolverId;
75099
75269
  const mcpClients = /* @__PURE__ */ new Map();
75100
75270
  const normalizedProviders = normalizeProviderOverrides(options.providers);
@@ -75142,7 +75312,7 @@ function tools(options = {}) {
75142
75312
  if (!inflight) inflight = (async () => {
75143
75313
  try {
75144
75314
  const { connections, catalog, catalogAvailable } = await loadSnapshotInputs();
75145
- const snapshot = await mapTools(connections, buildRequests(normalizedProviders, catalog, catalogAvailable), options, client, mcpClients, resolverId);
75315
+ const snapshot = await mapTools(connections, buildRequests(normalizedProviders, catalog, catalogAvailable), options, clientOptions, client, mcpClients, resolverId);
75146
75316
  cache = {
75147
75317
  snapshot,
75148
75318
  fetchedAt: Date.now()
@@ -75193,6 +75363,130 @@ function tools(options = {}) {
75193
75363
  return resolver;
75194
75364
  }
75195
75365
  /**
75366
+ * Most distinct options values a callback-form resolver keeps live caches
75367
+ * (and MCP clients) for at once. Callbacks are expected to produce a small,
75368
+ * closed set of option shapes (one per role, say); the cap exists so a
75369
+ * callback that accidentally derives options from unbounded per-request data
75370
+ * cannot leak an MCP client per request. Eviction is least-recently-used and
75371
+ * disconnects the evicted variant's transports.
75372
+ */
75373
+ const MAX_OPTION_VARIANTS = 32;
75374
+ /**
75375
+ * Builds the callback-form resolver: each resolution invokes `getOptions`
75376
+ * with the request context, then delegates to a memoized static resolver for
75377
+ * that options value, so every distinct options value gets its own snapshot
75378
+ * cache with the normal TTL / stale-while-revalidate semantics. Options
75379
+ * validation happens when a value is first seen (inside `tools()`), so
75380
+ * configuration errors fail that resolution loudly instead of being
75381
+ * swallowed.
75382
+ */
75383
+ function dynamicTools(getOptions) {
75384
+ const variants = /* @__PURE__ */ new Map();
75385
+ let closing;
75386
+ const disconnectEvicted = (entry) => {
75387
+ entry.resolver.disconnect().catch((error) => {
75388
+ console.warn(`[@mastra/connect] Failed to disconnect evicted tools variant: ${error instanceof Error ? error.message : String(error)}`);
75389
+ });
75390
+ };
75391
+ const releaseVariant = (entry) => {
75392
+ entry.inUse -= 1;
75393
+ if (entry.evicted && entry.inUse === 0) disconnectEvicted(entry);
75394
+ };
75395
+ const acquireVariant = async (ctx) => {
75396
+ if (closing) await closing;
75397
+ const options = await getOptions(ctx);
75398
+ if (closing) await closing;
75399
+ if (typeof options !== "object" || options === null || Array.isArray(options)) throw new MastraConnectError("invalid_options", `The tools() options callback must return a ToolsOptions object, got ${options === null ? "null" : Array.isArray(options) ? "an array" : typeof options}.`);
75400
+ const key = hashOptions(options);
75401
+ const existing = variants.get(key);
75402
+ if (existing) {
75403
+ variants.delete(key);
75404
+ variants.set(key, existing);
75405
+ existing.inUse += 1;
75406
+ return existing;
75407
+ }
75408
+ const entry = {
75409
+ resolver: tools(options),
75410
+ inUse: 1,
75411
+ evicted: false
75412
+ };
75413
+ variants.set(key, entry);
75414
+ if (variants.size > MAX_OPTION_VARIANTS) {
75415
+ const [oldestKey, oldest] = variants.entries().next().value;
75416
+ variants.delete(oldestKey);
75417
+ console.warn(`[@mastra/connect] tools() options callback produced more than ${MAX_OPTION_VARIANTS} distinct options values; evicting the least recently used variant. Derive options from a small, closed set of inputs (e.g. roles), not per-request data.`);
75418
+ oldest.evicted = true;
75419
+ if (oldest.inUse === 0) disconnectEvicted(oldest);
75420
+ }
75421
+ return entry;
75422
+ };
75423
+ const resolve = async (ctx) => {
75424
+ const entry = await acquireVariant(ctx);
75425
+ try {
75426
+ return await entry.resolver(ctx);
75427
+ } finally {
75428
+ releaseVariant(entry);
75429
+ }
75430
+ };
75431
+ const resolver = Object.assign(resolve, {
75432
+ refresh: async () => {
75433
+ const entry = await acquireVariant(void 0);
75434
+ try {
75435
+ return await entry.resolver.refresh();
75436
+ } finally {
75437
+ releaseVariant(entry);
75438
+ }
75439
+ },
75440
+ disconnect: () => {
75441
+ closing ??= (async () => {
75442
+ try {
75443
+ const all = Array.from(variants.values());
75444
+ variants.clear();
75445
+ await Promise.allSettled(all.map((entry) => entry.resolver.disconnect()));
75446
+ } finally {
75447
+ closing = void 0;
75448
+ }
75449
+ })();
75450
+ return closing;
75451
+ },
75452
+ with: (extra) => withExtraTools(resolver, extra)
75453
+ });
75454
+ return resolver;
75455
+ }
75456
+ /** Identity registry for non-serializable option values (e.g. a custom `client.fetch`). */
75457
+ const functionIdentity = /* @__PURE__ */ new WeakMap();
75458
+ let nextFunctionIdentity = 0;
75459
+ /**
75460
+ * Stable key for an options value: JSON with sorted object keys, so two
75461
+ * callbacks returning structurally equal options share one cached variant.
75462
+ * Functions (and other non-plain values) hash by identity — the same
75463
+ * instance maps to the same key, two different instances to different keys —
75464
+ * which is the safe direction: at worst a duplicate cache entry, never a
75465
+ * false merge of different configurations.
75466
+ */
75467
+ function hashOptions(options) {
75468
+ const byIdentity = (value) => {
75469
+ let id = functionIdentity.get(value);
75470
+ if (id === void 0) {
75471
+ id = ++nextFunctionIdentity;
75472
+ functionIdentity.set(value, id);
75473
+ }
75474
+ return `__identity_${id}__`;
75475
+ };
75476
+ const encode = (value) => {
75477
+ if (value === void 0) return "__undefined__";
75478
+ if (typeof value === "function") return byIdentity(value);
75479
+ if (Array.isArray(value)) return value.map(encode);
75480
+ if (typeof value === "object" && value !== null) {
75481
+ const proto = Object.getPrototypeOf(value);
75482
+ if (proto !== Object.prototype && proto !== null) return byIdentity(value);
75483
+ return Object.fromEntries(Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([entryKey, entryValue]) => [entryKey, encode(entryValue)]));
75484
+ }
75485
+ return value;
75486
+ };
75487
+ return JSON.stringify(encode(options));
75488
+ }
75489
+ /**
75196
75490
  * Builds the resolver `.with()` returns: resolutions merge the base
75197
75491
  * resolver's tools with `extra` (extra wins on key collision), cache handles
75198
75492
  * delegate to the base, and further `.with()` calls chain.
@@ -75323,7 +75617,7 @@ function buildRequests(providers, catalog, catalogAvailable) {
75323
75617
  return requests;
75324
75618
  }
75325
75619
  /** Maps one platform connection list snapshot to a flat tool record without allowing ambiguous tool ownership. */
75326
- async function mapTools(connections, requests, options, client, mcpClients, resolverId) {
75620
+ async function mapTools(connections, requests, options, clientOptions, client, mcpClients, resolverId) {
75327
75621
  const byIntegrationId = groupByIntegrationId(connections);
75328
75622
  const activeMcpKeys = /* @__PURE__ */ new Set();
75329
75623
  const result = {};
@@ -75356,7 +75650,7 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
75356
75650
  } else {
75357
75651
  providerTools = request.registration.createTools({
75358
75652
  connectionId: resolution.connectionId,
75359
- client: options.client,
75653
+ client: clientOptions,
75360
75654
  ...providerFilterOptions(request.options)
75361
75655
  });
75362
75656
  applyProxyRequireApproval(providerTools, integrationId, request.options.requireApproval);
@@ -75379,7 +75673,7 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
75379
75673
  connections: resolution.connections,
75380
75674
  allowTools: request.options.allowTools,
75381
75675
  disallowTools: request.options.disallowTools,
75382
- client: options.client
75676
+ client: clientOptions
75383
75677
  });
75384
75678
  applyProxyRequireApproval(providerTools, integrationId, request.options.requireApproval, [listConnectionsToolKey(integrationId)]);
75385
75679
  }
@@ -75581,7 +75875,10 @@ async function channels(options = {}) {
75581
75875
  const unknownChannelIds = Object.keys(providerOverrides).filter((integrationId) => !knownChannelIds.has(integrationId) && !providerOverrides[integrationId]?.disabled);
75582
75876
  if (unknownChannelIds.length > 0) throw new MastraConnectError("invalid_options", `Unknown channel${unknownChannelIds.length > 1 ? "s" : ""} in the providers option: ${unknownChannelIds.map((id) => `'${id}'`).join(", ")}. Known channels: ${[...knownChannelIds].join(", ")}.`);
75583
75877
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
75584
- const client = resolveClient(options.client);
75878
+ const client = resolveClient({
75879
+ ...options.client,
75880
+ projectId
75881
+ });
75585
75882
  const states = [];
75586
75883
  for (const registration of CHANNELS) {
75587
75884
  if (only !== void 0 && !only.has(registration.integrationId)) continue;