@mastra/editor 0.16.0-alpha.3 → 0.16.0-alpha.5

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/composio.cjs CHANGED
@@ -44,6 +44,7 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
44
44
  this.mastraClient = null;
45
45
  this.apiKey = config.apiKey;
46
46
  this.userIdResolver = config.userIdResolver;
47
+ this.sharedConnections = config.sharedConnections;
47
48
  }
48
49
  getRawClient() {
49
50
  if (!this.rawClient) this.rawClient = new _composio_core.Composio({ apiKey: this.apiKey });
@@ -189,10 +190,19 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
189
190
  authScheme,
190
191
  val: opts.config
191
192
  } : void 0;
193
+ const createShared = opts.scope === "shared" && this.sharedConnections !== void 0;
194
+ if (createShared && initiateConfig) throw new Error(`[composio] Cannot create a shared connection for toolkit "${opts.toolkit}": Composio SHARED accounts do not support custom connection fields.`);
195
+ const acl = this.sharedConnections?.acl;
192
196
  const request = initiateConfig ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {
193
197
  allowMultiple: true,
194
198
  config: initiateConfig
195
- }) : await composio.connectedAccounts.link(internalUserId, authConfigId);
199
+ }) : await composio.connectedAccounts.link(internalUserId, authConfigId, {
200
+ allowMultiple: true,
201
+ ...createShared ? { experimental: {
202
+ accountType: "SHARED",
203
+ ...acl ? { aclConfigForShared: acl } : {}
204
+ } } : {}
205
+ });
196
206
  if (!request.redirectUrl) throw new Error(`[composio] authorize did not return a redirectUrl for toolkit "${opts.toolkit}"`);
197
207
  return {
198
208
  url: request.redirectUrl,
@@ -227,7 +237,10 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
227
237
  if (opts.items.length === 0) return {};
228
238
  const composio = this.getRawClient();
229
239
  const toolkitSlugs = Array.from(new Set(opts.items.map((i) => i.toolkit)));
230
- const list = await composio.connectedAccounts.list({ toolkitSlugs });
240
+ const list = await composio.connectedAccounts.list({
241
+ toolkitSlugs,
242
+ accountType: "ALL"
243
+ });
231
244
  const liveById = /* @__PURE__ */ new Map();
232
245
  for (const item of list.items) liveById.set(item.id, {
233
246
  status: item.status,
@@ -253,24 +266,44 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
253
266
  hasMore: false
254
267
  }
255
268
  };
256
- const list = await composio.connectedAccounts.list({
269
+ const lists = [await composio.connectedAccounts.list({
257
270
  toolkitSlugs: [opts.toolkit],
258
271
  ...userIds ? { userIds } : {},
272
+ accountType: "ALL",
259
273
  limit: perPage
260
- });
261
- const items = (list.items ?? []).map((account) => ({
262
- connectionId: account.id,
263
- status: mapComposioStatus(account.status, account.isDisabled),
264
- createdAt: account.createdAt,
265
- authorId: account.user_id
266
- }));
267
- const nextCursor = list.nextCursor ?? null;
274
+ })];
275
+ if (userIds) {
276
+ const shared = await composio.connectedAccounts.list({
277
+ toolkitSlugs: [opts.toolkit],
278
+ accountType: "SHARED",
279
+ limit: perPage
280
+ });
281
+ lists.push({
282
+ ...shared,
283
+ items: (shared.items ?? []).filter((account) => userIds.some((userId) => isSharedAccountAccessible(account.experimental?.aclConfigForShared, userId)))
284
+ });
285
+ }
286
+ const items = [];
287
+ const seen = /* @__PURE__ */ new Set();
288
+ for (const account of lists.flatMap((list) => list.items ?? [])) {
289
+ if (seen.has(account.id)) continue;
290
+ seen.add(account.id);
291
+ items.push({
292
+ connectionId: account.id,
293
+ status: mapComposioStatus(account.status, account.isDisabled),
294
+ createdAt: account.createdAt,
295
+ authorId: account.user_id
296
+ });
297
+ }
268
298
  return {
269
299
  items,
270
300
  pagination: {
271
301
  page,
272
302
  perPage,
273
- hasMore: typeof nextCursor === "string" && nextCursor.length > 0
303
+ hasMore: lists.some((list) => {
304
+ const nextCursor = list.nextCursor ?? null;
305
+ return typeof nextCursor === "string" && nextCursor.length > 0;
306
+ })
274
307
  }
275
308
  };
276
309
  }
@@ -324,6 +357,17 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
324
357
  }
325
358
  };
326
359
  /**
360
+ * Composio's ACL rule for SHARED accounts: deny list wins, then
361
+ * `allowAllUsers`, then the allow list, otherwise deny. Owners are not covered
362
+ * here; they are listed by bucket.
363
+ */
364
+ function isSharedAccountAccessible(acl, userId) {
365
+ if (!acl) return false;
366
+ if (acl.notAllowedUserIds?.includes(userId)) return false;
367
+ if (acl.allowAllUsers) return true;
368
+ return acl.allowedUserIds?.includes(userId) ?? false;
369
+ }
370
+ /**
327
371
  * Best-effort 404 detection across the various error shapes the Composio
328
372
  * SDK surfaces (typed error with `statusCode`, HTTP-like error with
329
373
  * `status`, or a plain message containing "404" / "not found").
@@ -1 +1 @@
1
- {"version":3,"file":"composio.cjs","names":["BaseToolProvider","Composio","MastraProvider","MASTRA_RESOURCE_ID_KEY"],"sources":["../src/providers/composio.ts"],"sourcesContent":["import type {\n AuthFlowStatus,\n AuthorizeOpts,\n ConnectionField,\n ExistingConnection,\n ListConnectionsOpts,\n ListConnectionsResult,\n ListToolsOpts,\n ListToolsResult,\n ResolveToolsOpts,\n ToolProviderCapabilities,\n ToolProviderHealth,\n ToolProviderInfo,\n ToolProviderToolkit,\n BaseToolProviderOptions,\n} from '@mastra/core/tool-provider';\nimport { BaseToolProvider } from '@mastra/core/tool-provider';\nimport type { ToolAction } from '@mastra/core/tools';\nimport { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n /**\n * Server-side resolver mapping request context to the Composio `userId` the\n * call should execute as. Runs for `kind: 'invoker'` and `caller-supplied`\n * resolution, so the host application (for example, its FGA layer) can\n * derive and authorize the effective user before execution.\n *\n * Only server-populated fields within request context are trusted. When the\n * resolver is absent (or returns `undefined`), invoker connections require\n * the authenticated user (`MASTRA_USER_KEY`). Legacy `caller-supplied`\n * connections retain their existing resource-id fallback.\n *\n * The exact `connectedAccountId` always comes from the stored connection\n * pin — the resolver cannot override it.\n */\n userIdResolver?: ComposioUserIdResolver;\n}\n\n/** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */\nexport interface ComposioUserIdResolverInput {\n /** Live per-request context. Use `get()` for declared keys and `getRaw()` for reserved runtime keys. */\n requestContext?: RequestContext;\n /** Toolkit slug the identity is being resolved for, when known. */\n toolkit?: string;\n /**\n * The stored connection pin being resolved, when one exists. Hosts can use\n * it to validate that the invoker is allowed to use this exact account.\n */\n connectedAccountId?: string;\n}\n\n/**\n * Server-side resolver returning the effective Composio `userId` for a\n * request. Returning `undefined` falls back to the provider's default\n * identity resolution. Must never trust client-supplied context values.\n */\nexport type ComposioUserIdResolver = (\n input: ComposioUserIdResolverInput,\n) => Promise<string | undefined> | string | undefined;\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\nconst COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = new Set(['COMPOSIO_MANAGE_CONNECTIONS', 'COMPOSIO_WAIT_FOR_CONNECTIONS']);\n\n/**\n * Composio implementation of the {@link BaseToolProvider} contract.\n *\n * Discovery (`listAllToolkits`, `listAllTools`) uses the raw Composio\n * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved\n * tools are already in `createTool()` shape. Ordinary tools use Composio's\n * direct-tools API, while connection-management tools use a caller-scoped\n * Tool Router session. Resolved tools keep the `outputSchema` supplied by\n * `@composio/mastra`, which pre-relaxes Composio's strict API schemas\n * (nullable fields, extra properties, no `required`) so real third-party\n * responses validate while structurally invalid output is still rejected.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n readonly userIdResolver?: ComposioUserIdResolver;\n\n private readonly apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n defaultScope: config.defaultScope,\n });\n this.apiKey = config.apiKey;\n this.userIdResolver = config.userIdResolver;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n const rawTools: ComposioTool[] = await composio.tools.getRawComposioTools(query);\n\n const data = rawTools.map(tool => ({\n slug: tool.slug,\n name: tool.name ?? tool.slug,\n description: tool.description,\n toolkit: tool.toolkit?.slug ?? opts.toolkit ?? '',\n }));\n\n return {\n data,\n pagination: {\n page: opts.page ?? 1,\n perPage: limit,\n hasMore: limit !== undefined && rawTools.length >= limit,\n },\n };\n }\n\n // ── runtime ───────────────────────────────────────────────────────────\n\n async resolveToolsVNext(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>> {\n if (opts.toolSlugs.length === 0) return {};\n\n const identity = await this.resolveExecutionIdentity(opts);\n const composio = this.getMastraClient();\n const sessionToolSlugs = opts.toolSlugs.filter(slug => COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const directToolSlugs = opts.toolSlugs.filter(slug => !COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const mastraTools: MastraToolCollection = {};\n\n if (directToolSlugs.length > 0) {\n const modifiers = {\n // `connectedAccountId` is not threaded through Composio's `execute`\n // option bag in @composio/mastra; the only documented per-call hook\n // is `beforeExecute`, which receives the params object that flows\n // into the API call. Mutating `params.connectedAccountId` routes\n // the call to a specific account.\n beforeExecute: ({ params }: { params: { connectedAccountId?: string; userId?: string } }) => {\n if (identity.connectionId) {\n params.connectedAccountId = identity.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(identity.userId, { tools: directToolSlugs }, modifiers)) as MastraToolCollection,\n );\n }\n\n if (sessionToolSlugs.length > 0) {\n const selectedToolkits = [\n ...new Set(\n Object.values(opts.toolMeta)\n .map(meta => meta.toolkit)\n .filter(\n (toolkit): toolkit is string =>\n typeof toolkit === 'string' && toolkit.toLowerCase() !== COMPOSIO_PROVIDER_ID,\n ),\n ),\n ];\n const session = await composio.sessions.create(identity.userId, {\n ...(selectedToolkits.length > 0 ? { toolkits: selectedToolkits } : {}),\n manageConnections: { enable: true, waitForConnections: true },\n sandbox: { enable: false },\n });\n const sessionTools = (await session.tools()) as MastraToolCollection;\n\n for (const slug of sessionToolSlugs) {\n const tool = sessionTools[slug];\n if (tool) mastraTools[slug] = tool;\n }\n }\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools)) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n /**\n * Run the configured `userIdResolver` and validate its result. Returns\n * the resolved user id, or `undefined` when no resolver is configured or\n * the resolver declined (returned `undefined`). Throws when the resolver\n * returns an empty or non-string value — an empty execution identity must\n * fail closed instead of silently falling back.\n */\n private async runUserIdResolver(input: ComposioUserIdResolverInput): Promise<string | undefined> {\n if (!this.userIdResolver) return undefined;\n const resolved = await this.userIdResolver(input);\n if (resolved === undefined) return undefined;\n if (typeof resolved !== 'string') {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n const normalized = resolved.trim();\n if (normalized.length === 0) {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n return normalized;\n }\n\n /**\n * Resolve the effective Composio execution identity for one\n * `resolveToolsVNext` call: the `userId` bucket to fetch tools under and\n * the exact `connectedAccountId` to route execution to (absent = let\n * Composio auto-resolve within the bucket).\n */\n private async resolveExecutionIdentity(opts: ResolveToolsOpts): Promise<{ userId: string; connectionId?: string }> {\n // The unpinned caller-supplied bootstrap fan-out passes the user bucket\n // itself as `connectionId` (connectionId === authorId). That is not an\n // account pin, so execution must stay on Composio's per-bucket\n // auto-resolve.\n const hasAccountPin = opts.connectionId !== opts.authorId;\n\n if (opts.kind === 'invoker') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: opts.connectionId,\n });\n // Invoker connections execute as the authenticated user — never the\n // Memory resource id — against the exact stored account pin (which may\n // be an account another user shared with the invoker via Composio ACL).\n return {\n userId: resolvedUserId ?? resolveInvokerUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n if (opts.scope === 'caller-supplied') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: hasAccountPin ? opts.connectionId : undefined,\n });\n return {\n userId: resolvedUserId ?? resolveInternalUserId(opts.requestContext),\n connectionId: hasAccountPin ? opts.connectionId : undefined,\n };\n }\n\n // Author-bound (and legacy) connections: the runtime fan-out passes the\n // agent author's id explicitly. Use it as the Composio user bucket so the\n // pin resolves for any invoker (not just the original author), and always\n // route execution to the pinned account.\n return {\n userId: opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. When it is\n // present we must use `connectedAccounts.initiate`, which accepts a\n // discriminated `{ authScheme, val }` config for programmatic account\n // creation. Composio's non-deprecated `connectedAccounts.link` (hosted\n // Connect Link) has no `config` parameter, so it cannot carry these fields.\n const initiateConfig =\n opts.config && Object.keys(opts.config).length > 0 && authScheme\n ? ({ authScheme, val: opts.config } as unknown as Parameters<\n typeof composio.connectedAccounts.initiate\n >[2] extends infer O\n ? O extends { config?: infer C }\n ? C\n : never\n : never)\n : undefined;\n\n // Prefer `link` for the Composio-managed OAuth redirect flow: `initiate`\n // is deprecated for managed OAuth. `link` allows multiple connected\n // accounts per (user, auth config) by default, so we no longer pass\n // `allowMultiple`. Fall back to `initiate` only when custom `config` fields\n // are supplied, since `link` cannot forward them.\n const request = initiateConfig\n ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n config: initiateConfig,\n })\n : await composio.connectedAccounts.link(internalUserId, authConfigId);\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] authorize did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n limit: perPage,\n });\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = (list.items ?? []).map(account => ({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n }));\n\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n const hasMore = typeof nextCursor === 'string' && nextCursor.length > 0;\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\nfunction readAuthenticatedUserId(requestContext?: RequestContext): string | undefined {\n const user = requestContext?.getRaw(MASTRA_USER_KEY);\n if (!user || typeof user !== 'object' || !('id' in user)) return undefined;\n return typeof user.id === 'string' && user.id.length > 0 ? user.id : undefined;\n}\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: RequestContext): string {\n const resourceId = requestContext?.getRaw(MASTRA_RESOURCE_ID_KEY);\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n return readAuthenticatedUserId(requestContext) ?? DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Read the authenticated invoker's Composio `userId` from per-request\n * context. Invoker connections must never fall back to the Memory resource id\n * because a project or thread is not an authenticated connector principal.\n */\nfunction resolveInvokerUserId(requestContext?: RequestContext): string {\n const userId = readAuthenticatedUserId(requestContext);\n if (userId) return userId;\n throw new Error('[composio] kind \"invoker\" requires an authenticated user or a userIdResolver result');\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"composio.cjs","names":["BaseToolProvider","Composio","MastraProvider","MASTRA_RESOURCE_ID_KEY"],"sources":["../src/providers/composio.ts"],"sourcesContent":["import type {\n AuthFlowStatus,\n AuthorizeOpts,\n ConnectionField,\n ExistingConnection,\n ListConnectionsOpts,\n ListConnectionsResult,\n ListToolsOpts,\n ListToolsResult,\n ResolveToolsOpts,\n ToolProviderCapabilities,\n ToolProviderHealth,\n ToolProviderInfo,\n ToolProviderToolkit,\n BaseToolProviderOptions,\n} from '@mastra/core/tool-provider';\nimport { BaseToolProvider } from '@mastra/core/tool-provider';\nimport type { ToolAction } from '@mastra/core/tools';\nimport { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n /**\n * Server-side resolver mapping request context to the Composio `userId` the\n * call should execute as. Runs for `kind: 'invoker'` and `caller-supplied`\n * resolution, so the host application (for example, its FGA layer) can\n * derive and authorize the effective user before execution.\n *\n * Only server-populated fields within request context are trusted. When the\n * resolver is absent (or returns `undefined`), invoker connections require\n * the authenticated user (`MASTRA_USER_KEY`). Legacy `caller-supplied`\n * connections retain their existing resource-id fallback.\n *\n * The exact `connectedAccountId` always comes from the stored connection\n * pin — the resolver cannot override it.\n */\n userIdResolver?: ComposioUserIdResolver;\n /**\n * Opt in to Composio SHARED connected accounts. When set, connections\n * authorized with `scope: 'shared'` are created as Composio `SHARED`\n * accounts with the given ACL. Without it, shared-scope connections stay\n * Composio `PRIVATE` accounts under the shared bucket.\n *\n * Composio marks SHARED accounts as experimental.\n */\n sharedConnections?: ComposioSharedConnectionsConfig;\n}\n\n/** ACL applied to Composio SHARED accounts. Mirrors Composio's `aclConfigForShared`. */\nexport interface ComposioSharedAccountAcl {\n /** Allow every Composio `userId` (except `notAllowedUserIds`). */\n allowAllUsers?: boolean;\n /** Composio `userId`s allowed to use the account. */\n allowedUserIds?: string[];\n /** Composio `userId`s denied access. Deny always wins. */\n notAllowedUserIds?: string[];\n}\n\nexport interface ComposioSharedConnectionsConfig {\n /**\n * Initial ACL for newly created SHARED accounts. Omit to keep Composio's\n * deny-by-default (only the creating `userId` can use the account).\n */\n acl?: ComposioSharedAccountAcl;\n}\n\n/** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */\nexport interface ComposioUserIdResolverInput {\n /** Live per-request context. Use `get()` for declared keys and `getRaw()` for reserved runtime keys. */\n requestContext?: RequestContext;\n /** Toolkit slug the identity is being resolved for, when known. */\n toolkit?: string;\n /**\n * The stored connection pin being resolved, when one exists. Hosts can use\n * it to validate that the invoker is allowed to use this exact account.\n */\n connectedAccountId?: string;\n}\n\n/**\n * Server-side resolver returning the effective Composio `userId` for a\n * request. Returning `undefined` falls back to the provider's default\n * identity resolution. Must never trust client-supplied context values.\n */\nexport type ComposioUserIdResolver = (\n input: ComposioUserIdResolverInput,\n) => Promise<string | undefined> | string | undefined;\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\nconst COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = new Set(['COMPOSIO_MANAGE_CONNECTIONS', 'COMPOSIO_WAIT_FOR_CONNECTIONS']);\n\n/**\n * Composio implementation of the {@link BaseToolProvider} contract.\n *\n * Discovery (`listAllToolkits`, `listAllTools`) uses the raw Composio\n * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved\n * tools are already in `createTool()` shape. Ordinary tools use Composio's\n * direct-tools API, while connection-management tools use a caller-scoped\n * Tool Router session. Resolved tools keep the `outputSchema` supplied by\n * `@composio/mastra`, which pre-relaxes Composio's strict API schemas\n * (nullable fields, extra properties, no `required`) so real third-party\n * responses validate while structurally invalid output is still rejected.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n readonly userIdResolver?: ComposioUserIdResolver;\n\n private readonly apiKey: string;\n private readonly sharedConnections?: ComposioSharedConnectionsConfig;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n defaultScope: config.defaultScope,\n });\n this.apiKey = config.apiKey;\n this.userIdResolver = config.userIdResolver;\n this.sharedConnections = config.sharedConnections;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n const rawTools: ComposioTool[] = await composio.tools.getRawComposioTools(query);\n\n const data = rawTools.map(tool => ({\n slug: tool.slug,\n name: tool.name ?? tool.slug,\n description: tool.description,\n toolkit: tool.toolkit?.slug ?? opts.toolkit ?? '',\n }));\n\n return {\n data,\n pagination: {\n page: opts.page ?? 1,\n perPage: limit,\n hasMore: limit !== undefined && rawTools.length >= limit,\n },\n };\n }\n\n // ── runtime ───────────────────────────────────────────────────────────\n\n async resolveToolsVNext(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>> {\n if (opts.toolSlugs.length === 0) return {};\n\n const identity = await this.resolveExecutionIdentity(opts);\n const composio = this.getMastraClient();\n const sessionToolSlugs = opts.toolSlugs.filter(slug => COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const directToolSlugs = opts.toolSlugs.filter(slug => !COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const mastraTools: MastraToolCollection = {};\n\n if (directToolSlugs.length > 0) {\n const modifiers = {\n // `connectedAccountId` is not threaded through Composio's `execute`\n // option bag in @composio/mastra; the only documented per-call hook\n // is `beforeExecute`, which receives the params object that flows\n // into the API call. Mutating `params.connectedAccountId` routes\n // the call to a specific account.\n beforeExecute: ({ params }: { params: { connectedAccountId?: string; userId?: string } }) => {\n if (identity.connectionId) {\n params.connectedAccountId = identity.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(identity.userId, { tools: directToolSlugs }, modifiers)) as MastraToolCollection,\n );\n }\n\n if (sessionToolSlugs.length > 0) {\n const selectedToolkits = [\n ...new Set(\n Object.values(opts.toolMeta)\n .map(meta => meta.toolkit)\n .filter(\n (toolkit): toolkit is string =>\n typeof toolkit === 'string' && toolkit.toLowerCase() !== COMPOSIO_PROVIDER_ID,\n ),\n ),\n ];\n const session = await composio.sessions.create(identity.userId, {\n ...(selectedToolkits.length > 0 ? { toolkits: selectedToolkits } : {}),\n manageConnections: { enable: true, waitForConnections: true },\n sandbox: { enable: false },\n });\n const sessionTools = (await session.tools()) as MastraToolCollection;\n\n for (const slug of sessionToolSlugs) {\n const tool = sessionTools[slug];\n if (tool) mastraTools[slug] = tool;\n }\n }\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools)) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n /**\n * Run the configured `userIdResolver` and validate its result. Returns\n * the resolved user id, or `undefined` when no resolver is configured or\n * the resolver declined (returned `undefined`). Throws when the resolver\n * returns an empty or non-string value — an empty execution identity must\n * fail closed instead of silently falling back.\n */\n private async runUserIdResolver(input: ComposioUserIdResolverInput): Promise<string | undefined> {\n if (!this.userIdResolver) return undefined;\n const resolved = await this.userIdResolver(input);\n if (resolved === undefined) return undefined;\n if (typeof resolved !== 'string') {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n const normalized = resolved.trim();\n if (normalized.length === 0) {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n return normalized;\n }\n\n /**\n * Resolve the effective Composio execution identity for one\n * `resolveToolsVNext` call: the `userId` bucket to fetch tools under and\n * the exact `connectedAccountId` to route execution to (absent = let\n * Composio auto-resolve within the bucket).\n */\n private async resolveExecutionIdentity(opts: ResolveToolsOpts): Promise<{ userId: string; connectionId?: string }> {\n // The unpinned caller-supplied bootstrap fan-out passes the user bucket\n // itself as `connectionId` (connectionId === authorId). That is not an\n // account pin, so execution must stay on Composio's per-bucket\n // auto-resolve.\n const hasAccountPin = opts.connectionId !== opts.authorId;\n\n if (opts.kind === 'invoker') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: opts.connectionId,\n });\n // Invoker connections execute as the authenticated user — never the\n // Memory resource id — against the exact stored account pin (which may\n // be an account another user shared with the invoker via Composio ACL).\n return {\n userId: resolvedUserId ?? resolveInvokerUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n if (opts.scope === 'caller-supplied') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: hasAccountPin ? opts.connectionId : undefined,\n });\n return {\n userId: resolvedUserId ?? resolveInternalUserId(opts.requestContext),\n connectionId: hasAccountPin ? opts.connectionId : undefined,\n };\n }\n\n // Author-bound (and legacy) connections: the runtime fan-out passes the\n // agent author's id explicitly. Use it as the Composio user bucket so the\n // pin resolves for any invoker (not just the original author), and always\n // route execution to the pinned account.\n return {\n userId: opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. When it is\n // present we must use `connectedAccounts.initiate`, which accepts a\n // discriminated `{ authScheme, val }` config for programmatic account\n // creation. Composio's non-deprecated `connectedAccounts.link` (hosted\n // Connect Link) has no `config` parameter, so it cannot carry these fields.\n const initiateConfig =\n opts.config && Object.keys(opts.config).length > 0 && authScheme\n ? ({ authScheme, val: opts.config } as unknown as Parameters<\n typeof composio.connectedAccounts.initiate\n >[2] extends infer O\n ? O extends { config?: infer C }\n ? C\n : never\n : never)\n : undefined;\n\n // Only `link` accepts the `experimental` block, so a SHARED account that\n // also needs custom config fields cannot be created. Fail instead of\n // silently creating a PRIVATE account.\n const createShared = opts.scope === 'shared' && this.sharedConnections !== undefined;\n if (createShared && initiateConfig) {\n throw new Error(\n `[composio] Cannot create a shared connection for toolkit \"${opts.toolkit}\": Composio SHARED accounts do not support custom connection fields.`,\n );\n }\n\n // Prefer `link` for the Composio-managed OAuth redirect flow: `initiate`\n // is deprecated for managed OAuth. Both reject a second ACTIVE account per\n // (user, auth config) unless `allowMultiple` is set, and this provider\n // supports multiple connections per toolkit. Fall back to `initiate` only\n // when custom `config` fields are supplied, since `link` cannot forward them.\n const acl = this.sharedConnections?.acl;\n const request = initiateConfig\n ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n config: initiateConfig,\n })\n : await composio.connectedAccounts.link(internalUserId, authConfigId, {\n allowMultiple: true,\n ...(createShared\n ? { experimental: { accountType: 'SHARED' as const, ...(acl ? { aclConfigForShared: acl } : {}) } }\n : {}),\n });\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] authorize did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n // `accountType: 'ALL'` so pinned SHARED accounts resolve too; Composio\n // lists PRIVATE accounts only by default.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n accountType: 'ALL',\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n //\n // Composio lists PRIVATE accounts only by default, so request `'ALL'` to\n // include SHARED accounts owned by the requested buckets.\n const owned: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n accountType: 'ALL',\n limit: perPage,\n });\n const lists = [owned];\n\n // SHARED accounts owned by other users are usable by the requested buckets\n // when their ACL grants access. List them separately and evaluate the ACL\n // locally so the result doesn't depend on how Composio combines `userIds`\n // with `accountType`.\n if (userIds) {\n const shared: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n accountType: 'SHARED',\n limit: perPage,\n });\n lists.push({\n ...shared,\n items: (shared.items ?? []).filter(account =>\n userIds.some(userId => isSharedAccountAccessible(account.experimental?.aclConfigForShared, userId)),\n ),\n });\n }\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = [];\n const seen = new Set<string>();\n for (const account of lists.flatMap(list => list.items ?? [])) {\n if (seen.has(account.id)) continue;\n seen.add(account.id);\n items.push({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n });\n }\n\n const hasMore = lists.some(list => {\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n return typeof nextCursor === 'string' && nextCursor.length > 0;\n });\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Composio's ACL rule for SHARED accounts: deny list wins, then\n * `allowAllUsers`, then the allow list, otherwise deny. Owners are not covered\n * here; they are listed by bucket.\n */\nfunction isSharedAccountAccessible(acl: Partial<ComposioSharedAccountAcl> | undefined, userId: string): boolean {\n if (!acl) return false;\n if (acl.notAllowedUserIds?.includes(userId)) return false;\n if (acl.allowAllUsers) return true;\n return acl.allowedUserIds?.includes(userId) ?? false;\n}\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\nfunction readAuthenticatedUserId(requestContext?: RequestContext): string | undefined {\n const user = requestContext?.getRaw(MASTRA_USER_KEY);\n if (!user || typeof user !== 'object' || !('id' in user)) return undefined;\n return typeof user.id === 'string' && user.id.length > 0 ? user.id : undefined;\n}\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: RequestContext): string {\n const resourceId = requestContext?.getRaw(MASTRA_RESOURCE_ID_KEY);\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n return readAuthenticatedUserId(requestContext) ?? DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Read the authenticated invoker's Composio `userId` from per-request\n * context. Invoker connections must never fall back to the Memory resource id\n * because a project or thread is not an authenticated connector principal.\n */\nfunction resolveInvokerUserId(requestContext?: RequestContext): string {\n const userId = readAuthenticatedUserId(requestContext);\n if (userId) return userId;\n throw new Error('[composio] kind \"invoker\" requires an authenticated user or a userIdResolver result');\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":";;;;;;AAoGA,MAAM,uBAAuB;AAC7B,MAAM,2BAA2B;AACjC,MAAM,uDAAuC,IAAI,IAAI,CAAC,+BAA+B,+BAA+B,CAAC;;;;;;;;;;;;;;;;AAiBrH,IAAa,uBAAb,cAA0CA,2BAAAA,iBAAiB;CAoBzD,YAAY,QAAoC;EAC9C,MAAM;GACJ,iBAAiB,OAAO;GACxB,cAAc,OAAO;GACrB,cAAc,OAAO;EACvB,CAAC;EAxB+B,KAAA,OAAA;GAChC,IAAI;GACJ,MAAM;GACN,aAAa;EACf;EACkD,KAAA,eAAA;GAChD,+BAA+B;GAC/B,uBAAuB;GACvB,+BAA+B;GAC/B,gBAAgB;EAClB;EAMqC,KAAA,YAAA;EACmB,KAAA,eAAA;EAQtD,KAAK,SAAS,OAAO;EACrB,KAAK,iBAAiB,OAAO;EAC7B,KAAK,oBAAoB,OAAO;CAClC;CAIA,eAAiC;EAC/B,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAIC,eAAAA,SAAS,EAAE,QAAQ,KAAK,OAAO,CAAC;EAEvD,OAAO,KAAK;CACd;CAEA,kBAAoD;EAClD,IAAI,CAAC,KAAK,cACR,KAAK,eAAe,IAAIA,eAAAA,SAAS;GAC/B,QAAQ,KAAK;GACb,UAAU,IAAIC,iBAAAA,eAAe;EAC/B,CAAC;EAEH,OAAO,KAAK;CACd;CAIA,MAAgB,kBAAkD;EAGhE,QAAO,MAFU,KAAK,aACuB,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAA,CAC9C,KAAI,QAAO;GACzB,MAAM,GAAG;GACT,MAAM,GAAG;GACT,aAAa,GAAG,MAAM;GACtB,MAAM,GAAG,MAAM;EACjB,EAAE;CACJ;CAEA,MAAgB,aAAa,MAA+C;EAC1E,MAAM,WAAW,KAAK,aAAa;EASnC,MAAM,QAAQ,KAAK;EACnB,MAAM,mBAAmB,KAAK,gBAAgB,SAAS,IAAI,CAAC,GAAG,KAAK,eAAe,IAAI,KAAA;EACvF,MAAM,QACJ,KAAK,UACD;GAAE,UAAU,CAAC,KAAK,OAAO;GAAG;GAAO,QAAQ,KAAK;EAAO,IACvD,mBACE;GAAE,UAAU;GAAkB;GAAO,QAAQ,KAAK;EAAO,IACzD,KAAK,SACH;GAAE,QAAQ,KAAK;GAAQ;EAAM,IAC7B;GAAE,UAAU,CAAC;GAAe;EAAM;EAG5C,MAAM,WAA2B,MAAM,SAAS,MAAM,oBAAoB,KAAK;EAS/E,OAAO;GACL,MARW,SAAS,KAAI,UAAS;IACjC,MAAM,KAAK;IACX,MAAM,KAAK,QAAQ,KAAK;IACxB,aAAa,KAAK;IAClB,SAAS,KAAK,SAAS,QAAQ,KAAK,WAAW;GACjD,EAGK;GACH,YAAY;IACV,MAAM,KAAK,QAAQ;IACnB,SAAS;IACT,SAAS,UAAU,KAAA,KAAa,SAAS,UAAU;GACrD;EACF;CACF;CAIA,MAAM,kBAAkB,MAA4E;EAClG,IAAI,KAAK,UAAU,WAAW,GAAG,OAAO,CAAC;EAEzC,MAAM,WAAW,MAAM,KAAK,yBAAyB,IAAI;EACzD,MAAM,WAAW,KAAK,gBAAgB;EACtC,MAAM,mBAAmB,KAAK,UAAU,QAAO,SAAQ,qCAAqC,IAAI,IAAI,CAAC;EACrG,MAAM,kBAAkB,KAAK,UAAU,QAAO,SAAQ,CAAC,qCAAqC,IAAI,IAAI,CAAC;EACrG,MAAM,cAAoC,CAAC;EAE3C,IAAI,gBAAgB,SAAS,GAe3B,OAAO,OACL,aACC,MAAM,SAAS,MAAM,IAAI,SAAS,QAAQ,EAAE,OAAO,gBAAgB,GAAG,EAVvE,gBAAgB,EAAE,aAA2E;GAC3F,IAAI,SAAS,cACX,OAAO,qBAAqB,SAAS;GAEvC,OAAO;EACT,EAK+E,CAAC,CAClF;EAGF,IAAI,iBAAiB,SAAS,GAAG;GAC/B,MAAM,mBAAmB,CACvB,GAAG,IAAI,IACL,OAAO,OAAO,KAAK,QAAQ,CAAC,CACzB,KAAI,SAAQ,KAAK,OAAO,CAAC,CACzB,QACE,YACC,OAAO,YAAY,YAAY,QAAQ,YAAY,MAAM,oBAC7D,CACJ,CACF;GAMA,MAAM,eAAgB,OAAM,MALN,SAAS,SAAS,OAAO,SAAS,QAAQ;IAC9D,GAAI,iBAAiB,SAAS,IAAI,EAAE,UAAU,iBAAiB,IAAI,CAAC;IACpE,mBAAmB;KAAE,QAAQ;KAAM,oBAAoB;IAAK;IAC5D,SAAS,EAAE,QAAQ,MAAM;GAC3B,CAAC,EAAA,CACmC,MAAM;GAE1C,KAAK,MAAM,QAAQ,kBAAkB;IACnC,MAAM,OAAO,aAAa;IAC1B,IAAI,MAAM,YAAY,QAAQ;GAChC;EACF;EAEA,MAAM,SAAoD,CAAC;EAE3D,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,WAAW,GAAG;GACrD,IAAI,CAAC,MAAM;GACX,MAAM,OAAQ,KAAyB,MAAM;GAE7C,MAAM,eAAe,KAAK,WAAW,KAAK,EAAE;GAC5C,IAAI,cACF,IAAI;IACF,KAA6C,cAAc;GAC7D,QAAQ,CAER;GAGF,OAAO,QAAQ;EACjB;EAEA,OAAO;CACT;;;;;;;;CASA,MAAc,kBAAkB,OAAiE;EAC/F,IAAI,CAAC,KAAK,gBAAgB,OAAO,KAAA;EACjC,MAAM,WAAW,MAAM,KAAK,eAAe,KAAK;EAChD,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;EACnC,IAAI,OAAO,aAAa,UACtB,MAAM,IAAI,MAAM,uEAAuE;EAEzF,MAAM,aAAa,SAAS,KAAK;EACjC,IAAI,WAAW,WAAW,GACxB,MAAM,IAAI,MAAM,uEAAuE;EAEzF,OAAO;CACT;;;;;;;CAQA,MAAc,yBAAyB,MAA4E;EAKjH,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAEjD,IAAI,KAAK,SAAS,WAShB,OAAO;GACL,QAAQ,MATmB,KAAK,kBAAkB;IAClD,gBAAgB,KAAK;IACrB,SAAS,KAAK;IACd,oBAAoB,KAAK;GAC3B,CAAC,KAK2B,qBAAqB,KAAK,cAAc;GAClE,cAAc,KAAK;EACrB;EAGF,IAAI,KAAK,UAAU,mBAMjB,OAAO;GACL,QAAQ,MANmB,KAAK,kBAAkB;IAClD,gBAAgB,KAAK;IACrB,SAAS,KAAK;IACd,oBAAoB,gBAAgB,KAAK,eAAe,KAAA;GAC1D,CAAC,KAE2B,sBAAsB,KAAK,cAAc;GACnE,cAAc,gBAAgB,KAAK,eAAe,KAAA;EACpD;EAOF,OAAO;GACL,QAAQ,KAAK,YAAY,KAAK,SAAS,SAAS,IAAI,KAAK,WAAW,sBAAsB,KAAK,cAAc;GAC7G,cAAc,KAAK;EACrB;CACF;CAIA,MAAM,UAAU,MAA+D;EAC7E,MAAM,WAAW,KAAK,aAAa;EACnC,MAAM,EAAE,IAAI,cAAc,eAAe,MAAM,KAAK,kBAAkB,KAAK,OAAO;EAKlF,MAAM,iBAAiB,KAAK,gBAAgB;EAQ5C,MAAM,iBACJ,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM,CAAC,CAAC,SAAS,KAAK,aACjD;GAAE;GAAY,KAAK,KAAK;EAAO,IAOhC,KAAA;EAKN,MAAM,eAAe,KAAK,UAAU,YAAY,KAAK,sBAAsB,KAAA;EAC3E,IAAI,gBAAgB,gBAClB,MAAM,IAAI,MACR,6DAA6D,KAAK,QAAQ,qEAC5E;EAQF,MAAM,MAAM,KAAK,mBAAmB;EACpC,MAAM,UAAU,iBACZ,MAAM,SAAS,kBAAkB,SAAS,gBAAgB,cAAc;GACtE,eAAe;GACf,QAAQ;EACV,CAAC,IACD,MAAM,SAAS,kBAAkB,KAAK,gBAAgB,cAAc;GAClE,eAAe;GACf,GAAI,eACA,EAAE,cAAc;IAAE,aAAa;IAAmB,GAAI,MAAM,EAAE,oBAAoB,IAAI,IAAI,CAAC;GAAG,EAAE,IAChG,CAAC;EACP,CAAC;EAEL,IAAI,CAAC,QAAQ,aACX,MAAM,IAAI,MAAM,kEAAkE,KAAK,QAAQ,EAAE;EAGnG,OAAO;GAAE,KAAK,QAAQ;GAAa,QAAQ,QAAQ;EAAG;CACxD;CAEA,MAAM,qBAAqB,EAAE,WAA4D;EACvF,MAAM,WAAW,KAAK,aAAa;EACnC,MAAM,EAAE,eAAe,MAAM,KAAK,kBAAkB,OAAO;EAC3D,IAAI,CAAC,YAGH,OAAO,CAAC;EAKV,QAAO,MAHc,SAAS,SAAS,oCAAoC,SAAS,YAAY,EAC9F,cAAc,MAChB,CAAC,EAAA,CACa,KAAI,OAAM;GACtB,MAAM,EAAE;GACR,aAAa,EAAE;GACf,aAAa,EAAE;GACf,MAAM,gBAAgB,EAAE,IAAI;GAC5B,UAAU,EAAE,YAAY;GACxB,SAAS,EAAE,WAAW,KAAA;EACxB,EAAE;CACJ;CAEA,MAAM,cAAc,QAAyC;EAG3D,SAAQ,MAFS,KAAK,aACO,CAAC,CAAC,kBAAkB,IAAI,MAAM,EAAA,CAC3C,QAAhB;GACE,KAAK,UACH,OAAO;GACT,KAAK;GACL,KAAK,aACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,YACH,OAAO;GACT,SACE,OAAO;EACX;CACF;CAEA,MAAM,oBAAoB,MAE0B;EAClD,IAAI,KAAK,MAAM,WAAW,GAAG,OAAO,CAAC;EAErC,MAAM,WAAW,KAAK,aAAa;EACnC,MAAM,eAAe,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAI,MAAK,EAAE,OAAO,CAAC,CAAC;EAMvE,MAAM,OAAqC,MAAM,SAAS,kBAAkB,KAAK;GAC/E;GACA,aAAa;EACf,CAAC;EAED,MAAM,2BAAW,IAAI,IAAqD;EAC1E,KAAK,MAAM,QAAQ,KAAK,OACtB,SAAS,IAAI,KAAK,IAAI;GAAE,QAAQ,KAAK;GAAQ,YAAY,KAAK;EAAW,CAAC;EAG5E,MAAM,SAAiD,CAAC;EACxD,KAAK,MAAM,EAAE,kBAAkB,KAAK,OAAO;GACzC,MAAM,OAAO,SAAS,IAAI,YAAY;GACtC,OAAO,gBAAgB,EAAE,WAAW,OAAO,KAAK,WAAW,YAAY,CAAC,KAAK,aAAa,MAAM;EAClG;EACA,OAAO;CACT;CAEA,MAAM,gBAAgB,MAA2D;EAC/E,MAAM,WAAW,KAAK,aAAa;EACnC,MAAM,OAAO,KAAK,QAAQ;EAC1B,MAAM,UAAU,WAAW,KAAK,OAAO;EAIvC,MAAM,UAAU,eAAe,IAAI;EACnC,IAAI,WAAW,QAAQ,WAAW,GAChC,OAAO;GAAE,OAAO,CAAC;GAAG,YAAY;IAAE;IAAM;IAAS,SAAS;GAAM;EAAE;EAgBpE,MAAM,QAAQ,CAAC,MANmC,SAAS,kBAAkB,KAAK;GAChF,cAAc,CAAC,KAAK,OAAO;GAC3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;GAC7B,aAAa;GACb,OAAO;EACT,CAAC,CACmB;EAMpB,IAAI,SAAS;GACX,MAAM,SAAuC,MAAM,SAAS,kBAAkB,KAAK;IACjF,cAAc,CAAC,KAAK,OAAO;IAC3B,aAAa;IACb,OAAO;GACT,CAAC;GACD,MAAM,KAAK;IACT,GAAG;IACH,QAAQ,OAAO,SAAS,CAAC,EAAA,CAAG,QAAO,YACjC,QAAQ,MAAK,WAAU,0BAA0B,QAAQ,cAAc,oBAAoB,MAAM,CAAC,CACpG;GACF,CAAC;EACH;EAIA,MAAM,QAA8B,CAAC;EACrC,MAAM,uBAAO,IAAI,IAAY;EAC7B,KAAK,MAAM,WAAW,MAAM,SAAQ,SAAQ,KAAK,SAAS,CAAC,CAAC,GAAG;GAC7D,IAAI,KAAK,IAAI,QAAQ,EAAE,GAAG;GAC1B,KAAK,IAAI,QAAQ,EAAE;GACnB,MAAM,KAAK;IACT,cAAc,QAAQ;IACtB,QAAQ,kBAAkB,QAAQ,QAAQ,QAAQ,UAAU;IAC5D,WAAW,QAAQ;IAGnB,UAAW,QAA4C;GACzD,CAAC;EACH;EAMA,OAAO;GAAE;GAAO,YAAY;IAAE;IAAM;IAAS,SAJ7B,MAAM,MAAK,SAAQ;KACjC,MAAM,aAAc,KAAwC,cAAc;KAC1E,OAAO,OAAO,eAAe,YAAY,WAAW,SAAS;IAC/D,CACmD;GAAE;EAAE;CACzD;;;;;;;;;;;CAYA,MAAM,iBAAiB,cAAqC;EAC1D,MAAM,WAAW,KAAK,aAAa;EACnC,IAAI;GACF,MAAM,MAAO,MAAM,SAAS,kBAAkB,OAAO,YAAY;GACjE,IAAI,OAAO,IAAI,YAAY,OACzB,MAAM,IAAI,MAAM,gDAAgD,aAAa,iBAAiB;EAElG,SAAS,KAAK;GACZ,IAAI,gBAAgB,GAAG,GAAG;GAC1B,MAAM;EACR;CACF;CAEA,MAAM,YAAyC;EAC7C,IAAI;GAEF,MADiB,KAAK,aACT,CAAC,CAAC,SAAS,IAAI,EAAE,OAAO,EAAE,CAAgD;GACvF,OAAO,EAAE,IAAI,KAAK;EACpB,SAAS,KAAK;GACZ,OAAO;IACL,IAAI;IACJ,SAAS,eAAe,QAAQ,IAAI,UAAU;GAChD;EACF;CACF;;;;;;CASA,MAAc,kBAAkB,SAA2E;EAGzG,MAAM,WAAU,MAFC,KAAK,aACQ,CAAC,CAAC,YAAY,KAAK,EAAE,QAAQ,CAAC,EAAA,CACnC,MAAM,QAAO,SAAQ,KAAK,WAAW,SAAS;EAEvE,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MACR,kDAAkD,QAAQ,yCAC5D;EAEF,IAAI,QAAQ,SAAS,GAAG;GACtB,MAAM,MAAM,QAAQ,KAAI,SAAQ,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI;GAClD,MAAM,IAAI,MACR,yDAAyD,QAAQ,KAAK,IAAI,6BAC5E;EACF;EACA,OAAO;GAAE,IAAI,QAAQ,EAAE,CAAE;GAAI,YAAY,QAAQ,EAAE,CAAE;EAAW;CAClE;AACF;;;;;;AAWA,SAAS,0BAA0B,KAAoD,QAAyB;CAC9G,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI,IAAI,mBAAmB,SAAS,MAAM,GAAG,OAAO;CACpD,IAAI,IAAI,eAAe,OAAO;CAC9B,OAAO,IAAI,gBAAgB,SAAS,MAAM,KAAK;AACjD;;;;;;AAOA,SAAS,gBAAgB,KAAuB;CAC9C,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,OAAO;CAC5C,MAAM,IAAI;CACV,IAAI,EAAE,eAAe,OAAO,EAAE,WAAW,KAAK,OAAO;CACrD,MAAM,MAAM,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,YAAY,IAAI;CACtE,OAAO,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,KAAK;AACxD;;;;;;AAOA,SAAS,gBAAgB,MAA+C;CACtE,QAAQ,KAAK,YAAY,GAAzB;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,SACH,OAAO;EACT,KAAK;EACL,KAAK,WACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;;AAMA,SAAS,kBAAkB,QAAgB,YAAmD;CAC5F,IAAI,YAAY,OAAO;CACvB,QAAQ,QAAR;EACE,KAAK,UACH,OAAO;EACT,KAAK;EACL,KAAK,aACH,OAAO;EACT,KAAK;EACL,KAAK,WACH,OAAO;EACT,KAAK,YACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAIA,MAAM,kBAAkB;AAExB,SAAS,wBAAwB,gBAAqD;CACpF,MAAM,OAAO,gBAAgB,OAAO,eAAe;CACnD,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,EAAE,QAAQ,OAAO,OAAO,KAAA;CACjE,OAAO,OAAO,KAAK,OAAO,YAAY,KAAK,GAAG,SAAS,IAAI,KAAK,KAAK,KAAA;AACvE;;;;;;;;AASA,SAAS,sBAAsB,gBAAyC;CACtE,MAAM,aAAa,gBAAgB,OAAOC,6BAAAA,sBAAsB;CAChE,IAAI,OAAO,eAAe,YAAY,WAAW,SAAS,GACxD,OAAO;CAGT,OAAO,wBAAwB,cAAc,KAAK;AACpD;;;;;;AAOA,SAAS,qBAAqB,gBAAyC;CACrE,MAAM,SAAS,wBAAwB,cAAc;CACrD,IAAI,QAAQ,OAAO;CACnB,MAAM,IAAI,MAAM,uFAAqF;AACvG;;;;;;;;;AAUA,SAAS,eAAe,MAAiD;CACvE,IAAI,MAAM,QAAQ,KAAK,OAAO,GAAG,OAAO,KAAK;CAC7C,IAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,SAAS,GAAG,OAAO,CAAC,KAAK,MAAM;CAClF,OAAO,CAAC,wBAAwB;AAClC;AAEA,MAAM,gBAAgB;AACtB,MAAM,YAAY;AAElB,SAAS,WAAW,OAAmC;CACrD,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GACnE,OAAO;CAET,OAAO,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,SAAS;AAC9C"}
@@ -20,6 +20,31 @@ interface ComposioToolProviderConfig extends BaseToolProviderOptions {
20
20
  * pin — the resolver cannot override it.
21
21
  */
22
22
  userIdResolver?: ComposioUserIdResolver;
23
+ /**
24
+ * Opt in to Composio SHARED connected accounts. When set, connections
25
+ * authorized with `scope: 'shared'` are created as Composio `SHARED`
26
+ * accounts with the given ACL. Without it, shared-scope connections stay
27
+ * Composio `PRIVATE` accounts under the shared bucket.
28
+ *
29
+ * Composio marks SHARED accounts as experimental.
30
+ */
31
+ sharedConnections?: ComposioSharedConnectionsConfig;
32
+ }
33
+ /** ACL applied to Composio SHARED accounts. Mirrors Composio's `aclConfigForShared`. */
34
+ interface ComposioSharedAccountAcl {
35
+ /** Allow every Composio `userId` (except `notAllowedUserIds`). */
36
+ allowAllUsers?: boolean;
37
+ /** Composio `userId`s allowed to use the account. */
38
+ allowedUserIds?: string[];
39
+ /** Composio `userId`s denied access. Deny always wins. */
40
+ notAllowedUserIds?: string[];
41
+ }
42
+ interface ComposioSharedConnectionsConfig {
43
+ /**
44
+ * Initial ACL for newly created SHARED accounts. Omit to keep Composio's
45
+ * deny-by-default (only the creating `userId` can use the account).
46
+ */
47
+ acl?: ComposioSharedAccountAcl;
23
48
  }
24
49
  /** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */
25
50
  interface ComposioUserIdResolverInput {
@@ -59,6 +84,7 @@ declare class ComposioToolProvider extends BaseToolProvider {
59
84
  readonly capabilities: ToolProviderCapabilities;
60
85
  readonly userIdResolver?: ComposioUserIdResolver;
61
86
  private readonly apiKey;
87
+ private readonly sharedConnections?;
62
88
  private rawClient;
63
89
  private mastraClient;
64
90
  constructor(config: ComposioToolProviderConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"composio.d.cts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;UAIF;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;UACT;UACA;EAER,YAAY,QAAQ;UAYZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EA2CrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6CvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EAyBvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EAgDpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
1
+ {"version":3,"file":"composio.d.cts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;;;;;;;EASjB,oBAAoB;;;UAIL;;EAEf;;EAEA;;EAEA;;UAGe;;;;;EAKf,MAAM;;;UAIS;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;mBACA;UACT;UACA;EAER,YAAY,QAAQ;UAaZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EA2CrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6DvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EA4BvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EA+EpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
@@ -20,6 +20,31 @@ interface ComposioToolProviderConfig extends BaseToolProviderOptions {
20
20
  * pin — the resolver cannot override it.
21
21
  */
22
22
  userIdResolver?: ComposioUserIdResolver;
23
+ /**
24
+ * Opt in to Composio SHARED connected accounts. When set, connections
25
+ * authorized with `scope: 'shared'` are created as Composio `SHARED`
26
+ * accounts with the given ACL. Without it, shared-scope connections stay
27
+ * Composio `PRIVATE` accounts under the shared bucket.
28
+ *
29
+ * Composio marks SHARED accounts as experimental.
30
+ */
31
+ sharedConnections?: ComposioSharedConnectionsConfig;
32
+ }
33
+ /** ACL applied to Composio SHARED accounts. Mirrors Composio's `aclConfigForShared`. */
34
+ interface ComposioSharedAccountAcl {
35
+ /** Allow every Composio `userId` (except `notAllowedUserIds`). */
36
+ allowAllUsers?: boolean;
37
+ /** Composio `userId`s allowed to use the account. */
38
+ allowedUserIds?: string[];
39
+ /** Composio `userId`s denied access. Deny always wins. */
40
+ notAllowedUserIds?: string[];
41
+ }
42
+ interface ComposioSharedConnectionsConfig {
43
+ /**
44
+ * Initial ACL for newly created SHARED accounts. Omit to keep Composio's
45
+ * deny-by-default (only the creating `userId` can use the account).
46
+ */
47
+ acl?: ComposioSharedAccountAcl;
23
48
  }
24
49
  /** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */
25
50
  interface ComposioUserIdResolverInput {
@@ -59,6 +84,7 @@ declare class ComposioToolProvider extends BaseToolProvider {
59
84
  readonly capabilities: ToolProviderCapabilities;
60
85
  readonly userIdResolver?: ComposioUserIdResolver;
61
86
  private readonly apiKey;
87
+ private readonly sharedConnections?;
62
88
  private rawClient;
63
89
  private mastraClient;
64
90
  constructor(config: ComposioToolProviderConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"composio.d.ts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;UAIF;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;UACT;UACA;EAER,YAAY,QAAQ;UAYZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EA2CrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6CvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EAyBvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EAgDpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
1
+ {"version":3,"file":"composio.d.ts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;;;;;;;EASjB,oBAAoB;;;UAIL;;EAEf;;EAEA;;EAEA;;UAGe;;;;;EAKf,MAAM;;;UAIS;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;mBACA;UACT;UACA;EAER,YAAY,QAAQ;UAaZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EA2CrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6DvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EA4BvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EA+EpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
package/dist/composio.js CHANGED
@@ -43,6 +43,7 @@ var ComposioToolProvider = class extends BaseToolProvider {
43
43
  this.mastraClient = null;
44
44
  this.apiKey = config.apiKey;
45
45
  this.userIdResolver = config.userIdResolver;
46
+ this.sharedConnections = config.sharedConnections;
46
47
  }
47
48
  getRawClient() {
48
49
  if (!this.rawClient) this.rawClient = new Composio({ apiKey: this.apiKey });
@@ -188,10 +189,19 @@ var ComposioToolProvider = class extends BaseToolProvider {
188
189
  authScheme,
189
190
  val: opts.config
190
191
  } : void 0;
192
+ const createShared = opts.scope === "shared" && this.sharedConnections !== void 0;
193
+ if (createShared && initiateConfig) throw new Error(`[composio] Cannot create a shared connection for toolkit "${opts.toolkit}": Composio SHARED accounts do not support custom connection fields.`);
194
+ const acl = this.sharedConnections?.acl;
191
195
  const request = initiateConfig ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {
192
196
  allowMultiple: true,
193
197
  config: initiateConfig
194
- }) : await composio.connectedAccounts.link(internalUserId, authConfigId);
198
+ }) : await composio.connectedAccounts.link(internalUserId, authConfigId, {
199
+ allowMultiple: true,
200
+ ...createShared ? { experimental: {
201
+ accountType: "SHARED",
202
+ ...acl ? { aclConfigForShared: acl } : {}
203
+ } } : {}
204
+ });
195
205
  if (!request.redirectUrl) throw new Error(`[composio] authorize did not return a redirectUrl for toolkit "${opts.toolkit}"`);
196
206
  return {
197
207
  url: request.redirectUrl,
@@ -226,7 +236,10 @@ var ComposioToolProvider = class extends BaseToolProvider {
226
236
  if (opts.items.length === 0) return {};
227
237
  const composio = this.getRawClient();
228
238
  const toolkitSlugs = Array.from(new Set(opts.items.map((i) => i.toolkit)));
229
- const list = await composio.connectedAccounts.list({ toolkitSlugs });
239
+ const list = await composio.connectedAccounts.list({
240
+ toolkitSlugs,
241
+ accountType: "ALL"
242
+ });
230
243
  const liveById = /* @__PURE__ */ new Map();
231
244
  for (const item of list.items) liveById.set(item.id, {
232
245
  status: item.status,
@@ -252,24 +265,44 @@ var ComposioToolProvider = class extends BaseToolProvider {
252
265
  hasMore: false
253
266
  }
254
267
  };
255
- const list = await composio.connectedAccounts.list({
268
+ const lists = [await composio.connectedAccounts.list({
256
269
  toolkitSlugs: [opts.toolkit],
257
270
  ...userIds ? { userIds } : {},
271
+ accountType: "ALL",
258
272
  limit: perPage
259
- });
260
- const items = (list.items ?? []).map((account) => ({
261
- connectionId: account.id,
262
- status: mapComposioStatus(account.status, account.isDisabled),
263
- createdAt: account.createdAt,
264
- authorId: account.user_id
265
- }));
266
- const nextCursor = list.nextCursor ?? null;
273
+ })];
274
+ if (userIds) {
275
+ const shared = await composio.connectedAccounts.list({
276
+ toolkitSlugs: [opts.toolkit],
277
+ accountType: "SHARED",
278
+ limit: perPage
279
+ });
280
+ lists.push({
281
+ ...shared,
282
+ items: (shared.items ?? []).filter((account) => userIds.some((userId) => isSharedAccountAccessible(account.experimental?.aclConfigForShared, userId)))
283
+ });
284
+ }
285
+ const items = [];
286
+ const seen = /* @__PURE__ */ new Set();
287
+ for (const account of lists.flatMap((list) => list.items ?? [])) {
288
+ if (seen.has(account.id)) continue;
289
+ seen.add(account.id);
290
+ items.push({
291
+ connectionId: account.id,
292
+ status: mapComposioStatus(account.status, account.isDisabled),
293
+ createdAt: account.createdAt,
294
+ authorId: account.user_id
295
+ });
296
+ }
267
297
  return {
268
298
  items,
269
299
  pagination: {
270
300
  page,
271
301
  perPage,
272
- hasMore: typeof nextCursor === "string" && nextCursor.length > 0
302
+ hasMore: lists.some((list) => {
303
+ const nextCursor = list.nextCursor ?? null;
304
+ return typeof nextCursor === "string" && nextCursor.length > 0;
305
+ })
273
306
  }
274
307
  };
275
308
  }
@@ -323,6 +356,17 @@ var ComposioToolProvider = class extends BaseToolProvider {
323
356
  }
324
357
  };
325
358
  /**
359
+ * Composio's ACL rule for SHARED accounts: deny list wins, then
360
+ * `allowAllUsers`, then the allow list, otherwise deny. Owners are not covered
361
+ * here; they are listed by bucket.
362
+ */
363
+ function isSharedAccountAccessible(acl, userId) {
364
+ if (!acl) return false;
365
+ if (acl.notAllowedUserIds?.includes(userId)) return false;
366
+ if (acl.allowAllUsers) return true;
367
+ return acl.allowedUserIds?.includes(userId) ?? false;
368
+ }
369
+ /**
326
370
  * Best-effort 404 detection across the various error shapes the Composio
327
371
  * SDK surfaces (typed error with `statusCode`, HTTP-like error with
328
372
  * `status`, or a plain message containing "404" / "not found").