@mastra/editor 0.13.13 → 0.13.14-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @mastra/editor
2
2
 
3
+ ## 0.13.14-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Keep agent snapshot updates as drafts until explicitly published. ([#21528](https://github.com/mastra-ai/mastra/pull/21528))
8
+
9
+ - Updated dependencies [[`7e096f0`](https://github.com/mastra-ai/mastra/commit/7e096f02f0dddbf09b85d306458351245ed2f886), [`8f0a332`](https://github.com/mastra-ai/mastra/commit/8f0a3321bf180368d76fe7b36aa1a8f60f00b6de), [`b098de9`](https://github.com/mastra-ai/mastra/commit/b098de9d7cb9f672e0883a5c716465a3a689693d), [`ef6e295`](https://github.com/mastra-ai/mastra/commit/ef6e295b59bc25a5b61b633a89c97bcfce9fb465), [`208e1b3`](https://github.com/mastra-ai/mastra/commit/208e1b39f30f4b386e494394e9d71d96f0f90241), [`c938d34`](https://github.com/mastra-ai/mastra/commit/c938d34739936c8ecbabd67ad6a4a4396f41c4c6), [`1d9a0ea`](https://github.com/mastra-ai/mastra/commit/1d9a0ea4a9901baee6cd56737243bd6d1f631ac0), [`3667679`](https://github.com/mastra-ai/mastra/commit/3667679db057edfb086846d13369fdda4902ad65), [`49696e8`](https://github.com/mastra-ai/mastra/commit/49696e8e42f870674a0a58f5abcd22cc54dd2864), [`512100a`](https://github.com/mastra-ai/mastra/commit/512100a7d8b7e9c920f2590c6b3612f5de0d3cff), [`9ef432b`](https://github.com/mastra-ai/mastra/commit/9ef432b6faa534b57b0d182a610e13dd9a7123ff), [`b9cf308`](https://github.com/mastra-ai/mastra/commit/b9cf30846f97f99ac1906ee8a68f4f2d117b0378)]:
10
+ - @mastra/core@1.60.0-alpha.2
11
+
12
+ ## 0.13.14-alpha.0
13
+
14
+ ### Patch Changes
15
+
16
+ - Fixed Composio tool results not being validated. Resolved Composio tools now keep the output schema supplied by Composio, so tool results are checked against it: real API responses (including null or extra fields) still pass, while structurally invalid output is rejected instead of being silently returned. This also lets Composio tools be used with APIs that require an output schema, like createStep(tool). ([#21511](https://github.com/mastra-ai/mastra/pull/21511))
17
+
18
+ - Updated dependencies [[`15101bb`](https://github.com/mastra-ai/mastra/commit/15101bb53c0d934f31af6b8813b88191e382a5e5), [`c2c3deb`](https://github.com/mastra-ai/mastra/commit/c2c3debcf670c7082d0a5e553aa99818a864698c), [`33374ba`](https://github.com/mastra-ai/mastra/commit/33374ba359e4fb13eaa918ae925fe167a3c55414), [`c5f964d`](https://github.com/mastra-ai/mastra/commit/c5f964d3f77064e978f8066ec506eed77ba5c63c), [`f8f653f`](https://github.com/mastra-ai/mastra/commit/f8f653f10980d01a73706cc3c8689ca5e40ce808)]:
19
+ - @mastra/core@1.60.0-alpha.1
20
+ - @mastra/mcp@1.16.0
21
+
3
22
  ## 0.13.13
4
23
 
5
24
  ### Patch Changes
package/dist/composio.cjs CHANGED
@@ -14,8 +14,10 @@ const COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = /* @__PURE__ */ new Set(["COMPOSIO_
14
14
  * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved
15
15
  * tools are already in `createTool()` shape. Ordinary tools use Composio's
16
16
  * direct-tools API, while connection-management tools use a caller-scoped
17
- * Tool Router session. `outputSchema` is cleared because Composio returns
18
- * union schemas that Mastra's runtime rejects.
17
+ * Tool Router session. Resolved tools keep the `outputSchema` supplied by
18
+ * `@composio/mastra`, which pre-relaxes Composio's strict API schemas
19
+ * (nullable fields, extra properties, no `required`) so real third-party
20
+ * responses validate while structurally invalid output is still rejected.
19
21
  *
20
22
  * Allowlist filtering is layered by {@link BaseToolProvider}; this class
21
23
  * never reads `allowedToolkits` / `allowedTools` directly.
@@ -130,9 +132,6 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
130
132
  for (const [key, tool] of Object.entries(mastraTools)) {
131
133
  if (!tool) continue;
132
134
  const slug = tool.id ?? key;
133
- try {
134
- tool.outputSchema = void 0;
135
- } catch {}
136
135
  const descOverride = opts.toolMeta?.[slug]?.description;
137
136
  if (descOverride) try {
138
137
  tool.description = descOverride;
@@ -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';\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\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. `outputSchema` is cleared because Composio returns\n * union schemas that Mastra's runtime rejects.\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 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 }\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 // Composio's SDK validates every tool's input/output schema against an\n // internal zod shape and throws on the first malformed tool — so one bad\n // toolkit can poison a multi-toolkit query. Treat validation errors as a\n // soft failure and return an empty page rather than a 500.\n let rawTools: ComposioTool[] = [];\n try {\n rawTools = await composio.tools.getRawComposioTools(query);\n } catch (err) {\n console.warn(\n `[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`,\n err,\n );\n }\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 // Caller-supplied scope is always owned by the authenticated caller's\n // resource id. Author-bound connections use the agent author's id so the\n // same pin resolves for every invoker of that agent.\n const callerPrincipalId =\n opts.scope === 'caller-supplied'\n ? resolveInternalUserId(opts.requestContext)\n : opts.authorId && opts.authorId.length > 0\n ? opts.authorId\n : resolveInternalUserId(opts.requestContext);\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 // Under `caller-supplied` scope the user bucket (`callerPrincipalId`,\n // resolved from the host app's resourceId) already scopes the call to\n // the right tenant. Pinning a specific `connectedAccountId` would defeat\n // Composio's per-user-bucket auto-resolve, so we let Composio pick the\n // connected account within the bucket instead of forcing one.\n if (opts.scope !== 'caller-supplied') {\n params.connectedAccountId = opts.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(callerPrincipalId, { 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(callerPrincipalId, {\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 // Composio returns union output schemas (`successful: true | false`) that\n // Mastra's runtime cannot validate; clearing avoids per-tool validation\n // errors at execute time. The property may be non-writable on some SDK\n // versions, so we swallow assignment errors.\n try {\n (tool as unknown as { outputSchema: unknown }).outputSchema = undefined;\n } catch {\n // ignore\n }\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 // ── 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 0.6.x 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\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?: Record<string, unknown>): string {\n const resourceId = requestContext?.[MASTRA_RESOURCE_ID_KEY];\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n const user = requestContext?.[MASTRA_USER_KEY];\n if (user && typeof user === 'object' && 'id' in user) {\n const id = (user as { id: unknown }).id;\n if (typeof id === 'string' && id.length > 0) {\n return id;\n }\n }\n\n return DEFAULT_INTERNAL_USER_ID;\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';\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\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 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 }\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 // Composio's SDK validates every tool's input/output schema against an\n // internal zod shape and throws on the first malformed tool — so one bad\n // toolkit can poison a multi-toolkit query. Treat validation errors as a\n // soft failure and return an empty page rather than a 500.\n let rawTools: ComposioTool[] = [];\n try {\n rawTools = await composio.tools.getRawComposioTools(query);\n } catch (err) {\n console.warn(\n `[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`,\n err,\n );\n }\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 // Caller-supplied scope is always owned by the authenticated caller's\n // resource id. Author-bound connections use the agent author's id so the\n // same pin resolves for every invoker of that agent.\n const callerPrincipalId =\n opts.scope === 'caller-supplied'\n ? resolveInternalUserId(opts.requestContext)\n : opts.authorId && opts.authorId.length > 0\n ? opts.authorId\n : resolveInternalUserId(opts.requestContext);\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 // Under `caller-supplied` scope the user bucket (`callerPrincipalId`,\n // resolved from the host app's resourceId) already scopes the call to\n // the right tenant. Pinning a specific `connectedAccountId` would defeat\n // Composio's per-user-bucket auto-resolve, so we let Composio pick the\n // connected account within the bucket instead of forcing one.\n if (opts.scope !== 'caller-supplied') {\n params.connectedAccountId = opts.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(callerPrincipalId, { 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(callerPrincipalId, {\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 // ── 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 0.6.x 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\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?: Record<string, unknown>): string {\n const resourceId = requestContext?.[MASTRA_RESOURCE_ID_KEY];\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n const user = requestContext?.[MASTRA_USER_KEY];\n if (user && typeof user === 'object' && 'id' in user) {\n const id = (user as { id: unknown }).id;\n if (typeof id === 'string' && id.length > 0) {\n return id;\n }\n }\n\n return DEFAULT_INTERNAL_USER_ID;\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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"}
@@ -12,8 +12,10 @@ interface ComposioToolProviderConfig extends BaseToolProviderOptions {
12
12
  * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved
13
13
  * tools are already in `createTool()` shape. Ordinary tools use Composio's
14
14
  * direct-tools API, while connection-management tools use a caller-scoped
15
- * Tool Router session. `outputSchema` is cleared because Composio returns
16
- * union schemas that Mastra's runtime rejects.
15
+ * Tool Router session. Resolved tools keep the `outputSchema` supplied by
16
+ * `@composio/mastra`, which pre-relaxes Composio's strict API schemas
17
+ * (nullable fields, extra properties, no `required`) so real third-party
18
+ * responses validate while structurally invalid output is still rejected.
17
19
  *
18
20
  * Allowlist filtering is layered by {@link BaseToolProvider}; this class
19
21
  * never reads `allowedToolkits` / `allowedTools` directly.
@@ -1 +1 @@
1
- {"version":3,"file":"composio.d.cts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;UA8BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;cAoBW,6BAA6B;WAC/B,MAAM;WAKN,cAAc;mBAON;UACT;UACA;EAEI,YAAA,QAAQ;UAWZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EAuDrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;EAoGlE,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":";;;UA8BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;;;cAsBW,6BAA6B;WAC/B,MAAM;WAKN,cAAc;mBAON;UACT;UACA;EAEI,YAAA,QAAQ;UAWZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EAuDrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;EA0FlE,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"}
@@ -12,8 +12,10 @@ interface ComposioToolProviderConfig extends BaseToolProviderOptions {
12
12
  * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved
13
13
  * tools are already in `createTool()` shape. Ordinary tools use Composio's
14
14
  * direct-tools API, while connection-management tools use a caller-scoped
15
- * Tool Router session. `outputSchema` is cleared because Composio returns
16
- * union schemas that Mastra's runtime rejects.
15
+ * Tool Router session. Resolved tools keep the `outputSchema` supplied by
16
+ * `@composio/mastra`, which pre-relaxes Composio's strict API schemas
17
+ * (nullable fields, extra properties, no `required`) so real third-party
18
+ * responses validate while structurally invalid output is still rejected.
17
19
  *
18
20
  * Allowlist filtering is layered by {@link BaseToolProvider}; this class
19
21
  * never reads `allowedToolkits` / `allowedTools` directly.
@@ -1 +1 @@
1
- {"version":3,"file":"composio.d.ts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;UA8BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;cAoBW,6BAA6B;WAC/B,MAAM;WAKN,cAAc;mBAON;UACT;UACA;EAEI,YAAA,QAAQ;UAWZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EAuDrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;EAoGlE,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":";;;UA8BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;;;cAsBW,6BAA6B;WAC/B,MAAM;WAKN,cAAc;mBAON;UACT;UACA;EAEI,YAAA,QAAQ;UAWZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EAuDrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;EA0FlE,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"}
package/dist/composio.js CHANGED
@@ -13,8 +13,10 @@ const COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = /* @__PURE__ */ new Set(["COMPOSIO_
13
13
  * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved
14
14
  * tools are already in `createTool()` shape. Ordinary tools use Composio's
15
15
  * direct-tools API, while connection-management tools use a caller-scoped
16
- * Tool Router session. `outputSchema` is cleared because Composio returns
17
- * union schemas that Mastra's runtime rejects.
16
+ * Tool Router session. Resolved tools keep the `outputSchema` supplied by
17
+ * `@composio/mastra`, which pre-relaxes Composio's strict API schemas
18
+ * (nullable fields, extra properties, no `required`) so real third-party
19
+ * responses validate while structurally invalid output is still rejected.
18
20
  *
19
21
  * Allowlist filtering is layered by {@link BaseToolProvider}; this class
20
22
  * never reads `allowedToolkits` / `allowedTools` directly.
@@ -129,9 +131,6 @@ var ComposioToolProvider = class extends BaseToolProvider {
129
131
  for (const [key, tool] of Object.entries(mastraTools)) {
130
132
  if (!tool) continue;
131
133
  const slug = tool.id ?? key;
132
- try {
133
- tool.outputSchema = void 0;
134
- } catch {}
135
134
  const descOverride = opts.toolMeta?.[slug]?.description;
136
135
  if (descOverride) try {
137
136
  tool.description = descOverride;
@@ -1 +1 @@
1
- {"version":3,"file":"composio.js","names":[],"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';\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\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. `outputSchema` is cleared because Composio returns\n * union schemas that Mastra's runtime rejects.\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 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 }\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 // Composio's SDK validates every tool's input/output schema against an\n // internal zod shape and throws on the first malformed tool — so one bad\n // toolkit can poison a multi-toolkit query. Treat validation errors as a\n // soft failure and return an empty page rather than a 500.\n let rawTools: ComposioTool[] = [];\n try {\n rawTools = await composio.tools.getRawComposioTools(query);\n } catch (err) {\n console.warn(\n `[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`,\n err,\n );\n }\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 // Caller-supplied scope is always owned by the authenticated caller's\n // resource id. Author-bound connections use the agent author's id so the\n // same pin resolves for every invoker of that agent.\n const callerPrincipalId =\n opts.scope === 'caller-supplied'\n ? resolveInternalUserId(opts.requestContext)\n : opts.authorId && opts.authorId.length > 0\n ? opts.authorId\n : resolveInternalUserId(opts.requestContext);\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 // Under `caller-supplied` scope the user bucket (`callerPrincipalId`,\n // resolved from the host app's resourceId) already scopes the call to\n // the right tenant. Pinning a specific `connectedAccountId` would defeat\n // Composio's per-user-bucket auto-resolve, so we let Composio pick the\n // connected account within the bucket instead of forcing one.\n if (opts.scope !== 'caller-supplied') {\n params.connectedAccountId = opts.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(callerPrincipalId, { 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(callerPrincipalId, {\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 // Composio returns union output schemas (`successful: true | false`) that\n // Mastra's runtime cannot validate; clearing avoids per-tool validation\n // errors at execute time. The property may be non-writable on some SDK\n // versions, so we swallow assignment errors.\n try {\n (tool as unknown as { outputSchema: unknown }).outputSchema = undefined;\n } catch {\n // ignore\n }\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 // ── 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 0.6.x 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\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?: Record<string, unknown>): string {\n const resourceId = requestContext?.[MASTRA_RESOURCE_ID_KEY];\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n const user = requestContext?.[MASTRA_USER_KEY];\n if (user && typeof user === 'object' && 'id' in user) {\n const id = (user as { id: unknown }).id;\n if (typeof id === 'string' && id.length > 0) {\n return id;\n }\n }\n\n return DEFAULT_INTERNAL_USER_ID;\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":";;;;;AAmCA,MAAM,uBAAuB;AAC7B,MAAM,2BAA2B;AACjC,MAAM,uDAAuC,IAAI,IAAI,CAAC,+BAA+B,+BAA+B,CAAC;;;;;;;;;;;;;;AAerH,IAAa,uBAAb,cAA0C,iBAAiB;CAiBzD,YAAY,QAAoC;EAC9C,MAAM;GACJ,iBAAiB,OAAO;GACxB,cAAc,OAAO;GACrB,cAAc,OAAO;EACvB,CAAC;EArB+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;EAGqC,KAAA,YAAA;EACmB,KAAA,eAAA;EAQtD,KAAK,SAAS,OAAO;CACvB;CAIA,eAAiC;EAC/B,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,SAAS,EAAE,QAAQ,KAAK,OAAO,CAAC;EAEvD,OAAO,KAAK;CACd;CAEA,kBAAoD;EAClD,IAAI,CAAC,KAAK,cACR,KAAK,eAAe,IAAI,SAAS;GAC/B,QAAQ,KAAK;GACb,UAAU,IAAI,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;EAO5C,IAAI,WAA2B,CAAC;EAChC,IAAI;GACF,WAAW,MAAM,SAAS,MAAM,oBAAoB,KAAK;EAC3D,SAAS,KAAK;GACZ,QAAQ,KACN,wDAAwD,KAAK,UAAU,KAAK,EAAE,0BAC9E,GACF;EACF;EASA,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;EAKzC,MAAM,oBACJ,KAAK,UAAU,oBACX,sBAAsB,KAAK,cAAc,IACzC,KAAK,YAAY,KAAK,SAAS,SAAS,IACtC,KAAK,WACL,sBAAsB,KAAK,cAAc;EACjD,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,GAoB3B,OAAO,OACL,aACC,MAAM,SAAS,MAAM,IAAI,mBAAmB,EAAE,OAAO,gBAAgB,GAAG,EAfzE,gBAAgB,EAAE,aAA2E;GAM3F,IAAI,KAAK,UAAU,mBACjB,OAAO,qBAAqB,KAAK;GAEnC,OAAO;EACT,EAKiF,CAAC,CACpF;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,mBAAmB;IAChE,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;GAM7C,IAAI;IACF,KAA+C,eAAe,KAAA;GAChE,QAAQ,CAER;GAEA,MAAM,eAAe,KAAK,WAAW,KAAK,EAAE;GAC5C,IAAI,cACF,IAAI;IACF,KAA6C,cAAc;GAC7D,QAAQ,CAER;GAGF,OAAO,QAAQ;EACjB;EAEA,OAAO;CACT;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;EAON,MAAM,UAAU,iBACZ,MAAM,SAAS,kBAAkB,SAAS,gBAAgB,cAAc;GACtE,eAAe;GACf,QAAQ;EACV,CAAC,IACD,MAAM,SAAS,kBAAkB,KAAK,gBAAgB,YAAY;EAEtE,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;EAIvE,MAAM,OAAqC,MAAM,SAAS,kBAAkB,KAAK,EAC/E,aACF,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;EAOpE,MAAM,OAAqC,MAAM,SAAS,kBAAkB,KAAK;GAC/E,cAAc,CAAC,KAAK,OAAO;GAC3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;GAC7B,OAAO;EACT,CAAC;EAID,MAAM,SAA+B,KAAK,SAAS,CAAC,EAAA,CAAG,KAAI,aAAY;GACrE,cAAc,QAAQ;GACtB,QAAQ,kBAAkB,QAAQ,QAAQ,QAAQ,UAAU;GAC5D,WAAW,QAAQ;GAGnB,UAAW,QAA4C;EACzD,EAAE;EAEF,MAAM,aAAc,KAAwC,cAAc;EAE1E,OAAO;GAAE;GAAO,YAAY;IAAE;IAAM;IAAS,SAD7B,OAAO,eAAe,YAAY,WAAW,SAAS;GACjB;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,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;;;;;;;;AASxB,SAAS,sBAAsB,gBAAkD;CAC/E,MAAM,aAAa,iBAAiB;CACpC,IAAI,OAAO,eAAe,YAAY,WAAW,SAAS,GACxD,OAAO;CAGT,MAAM,OAAO,iBAAiB;CAC9B,IAAI,QAAQ,OAAO,SAAS,YAAY,QAAQ,MAAM;EACpD,MAAM,KAAM,KAAyB;EACrC,IAAI,OAAO,OAAO,YAAY,GAAG,SAAS,GACxC,OAAO;CAEX;CAEA,OAAO;AACT;;;;;;;;;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"}
1
+ {"version":3,"file":"composio.js","names":[],"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';\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\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 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 }\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 // Composio's SDK validates every tool's input/output schema against an\n // internal zod shape and throws on the first malformed tool — so one bad\n // toolkit can poison a multi-toolkit query. Treat validation errors as a\n // soft failure and return an empty page rather than a 500.\n let rawTools: ComposioTool[] = [];\n try {\n rawTools = await composio.tools.getRawComposioTools(query);\n } catch (err) {\n console.warn(\n `[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`,\n err,\n );\n }\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 // Caller-supplied scope is always owned by the authenticated caller's\n // resource id. Author-bound connections use the agent author's id so the\n // same pin resolves for every invoker of that agent.\n const callerPrincipalId =\n opts.scope === 'caller-supplied'\n ? resolveInternalUserId(opts.requestContext)\n : opts.authorId && opts.authorId.length > 0\n ? opts.authorId\n : resolveInternalUserId(opts.requestContext);\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 // Under `caller-supplied` scope the user bucket (`callerPrincipalId`,\n // resolved from the host app's resourceId) already scopes the call to\n // the right tenant. Pinning a specific `connectedAccountId` would defeat\n // Composio's per-user-bucket auto-resolve, so we let Composio pick the\n // connected account within the bucket instead of forcing one.\n if (opts.scope !== 'caller-supplied') {\n params.connectedAccountId = opts.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(callerPrincipalId, { 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(callerPrincipalId, {\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 // ── 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 0.6.x 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\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?: Record<string, unknown>): string {\n const resourceId = requestContext?.[MASTRA_RESOURCE_ID_KEY];\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n const user = requestContext?.[MASTRA_USER_KEY];\n if (user && typeof user === 'object' && 'id' in user) {\n const id = (user as { id: unknown }).id;\n if (typeof id === 'string' && id.length > 0) {\n return id;\n }\n }\n\n return DEFAULT_INTERNAL_USER_ID;\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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"}
package/dist/index.cjs CHANGED
@@ -707,18 +707,15 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
707
707
  if (!await store.getById(input.id)) throw new Error(`Agent with id ${input.id} not found`);
708
708
  const providedConfig = getProvidedSnapshotFields(input, AGENT_SNAPSHOT_CONFIG_FIELDS);
709
709
  if ("workspace" in providedConfig) await this.ensureStoredWorkspaceRefs(providedConfig.workspace);
710
- const versionResult = Object.keys(providedConfig).length > 0 ? await createVersionFromSnapshotUpdate({
710
+ if (Object.keys(providedConfig).length > 0) await createVersionFromSnapshotUpdate({
711
711
  store,
712
712
  parentId: input.id,
713
713
  parentIdField: "agentId",
714
714
  snapshotFields: AGENT_SNAPSHOT_CONFIG_FIELDS,
715
715
  providedConfig
716
- }) : { versionCreated: false };
717
- const recordFields = getProvidedAgentRecordFields(input);
718
- if (recordFields || versionResult.versionCreated) await store.update({
719
- ...recordFields ?? { id: input.id },
720
- ...versionResult.versionCreated ? { activeVersionId: versionResult.version.id } : {}
721
716
  });
717
+ const recordFields = getProvidedAgentRecordFields(input);
718
+ if (recordFields) await store.update(recordFields);
722
719
  this._cache.delete(input.id);
723
720
  this.onCacheEvict(input.id);
724
721
  const existingCodeAgent = this.getCodeDefinedAgent(input.id);