@mastra/editor 0.15.0 → 0.15.1-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/composio.cjs CHANGED
@@ -83,12 +83,7 @@ var ComposioToolProvider = class extends _mastra_core_tool_provider.BaseToolProv
83
83
  toolkits: [],
84
84
  limit
85
85
  };
86
- let rawTools = [];
87
- try {
88
- rawTools = await composio.tools.getRawComposioTools(query);
89
- } catch (err) {
90
- console.warn(`[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`, err);
91
- }
86
+ const rawTools = await composio.tools.getRawComposioTools(query);
92
87
  return {
93
88
  data: rawTools.map((tool) => ({
94
89
  slug: tool.slug,
@@ -1 +1 @@
1
- {"version":3,"file":"composio.cjs","names":["BaseToolProvider","Composio","MastraProvider","MASTRA_RESOURCE_ID_KEY"],"sources":["../src/providers/composio.ts"],"sourcesContent":["import type {\n AuthFlowStatus,\n AuthorizeOpts,\n ConnectionField,\n ExistingConnection,\n ListConnectionsOpts,\n ListConnectionsResult,\n ListToolsOpts,\n ListToolsResult,\n ResolveToolsOpts,\n ToolProviderCapabilities,\n ToolProviderHealth,\n ToolProviderInfo,\n ToolProviderToolkit,\n BaseToolProviderOptions,\n} from '@mastra/core/tool-provider';\nimport { BaseToolProvider } from '@mastra/core/tool-provider';\nimport type { ToolAction } from '@mastra/core/tools';\nimport { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n /**\n * Server-side resolver mapping request context to the Composio `userId` the\n * call should execute as. Runs for `kind: 'invoker'` and `caller-supplied`\n * resolution, so the host application (for example, its FGA layer) can\n * derive and authorize the effective user before execution.\n *\n * Only server-populated fields within request context are trusted. When the\n * resolver is absent (or returns `undefined`), invoker connections require\n * the authenticated user (`MASTRA_USER_KEY`). Legacy `caller-supplied`\n * connections retain their existing resource-id fallback.\n *\n * The exact `connectedAccountId` always comes from the stored connection\n * pin — the resolver cannot override it.\n */\n userIdResolver?: ComposioUserIdResolver;\n}\n\n/** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */\nexport interface ComposioUserIdResolverInput {\n /** Live per-request context. Use `get()` for declared keys and `getRaw()` for reserved runtime keys. */\n requestContext?: RequestContext;\n /** Toolkit slug the identity is being resolved for, when known. */\n toolkit?: string;\n /**\n * The stored connection pin being resolved, when one exists. Hosts can use\n * it to validate that the invoker is allowed to use this exact account.\n */\n connectedAccountId?: string;\n}\n\n/**\n * Server-side resolver returning the effective Composio `userId` for a\n * request. Returning `undefined` falls back to the provider's default\n * identity resolution. Must never trust client-supplied context values.\n */\nexport type ComposioUserIdResolver = (\n input: ComposioUserIdResolverInput,\n) => Promise<string | undefined> | string | undefined;\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\nconst COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = new Set(['COMPOSIO_MANAGE_CONNECTIONS', 'COMPOSIO_WAIT_FOR_CONNECTIONS']);\n\n/**\n * Composio implementation of the {@link BaseToolProvider} contract.\n *\n * Discovery (`listAllToolkits`, `listAllTools`) uses the raw Composio\n * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved\n * tools are already in `createTool()` shape. Ordinary tools use Composio's\n * direct-tools API, while connection-management tools use a caller-scoped\n * Tool Router session. Resolved tools keep the `outputSchema` supplied by\n * `@composio/mastra`, which pre-relaxes Composio's strict API schemas\n * (nullable fields, extra properties, no `required`) so real third-party\n * responses validate while structurally invalid output is still rejected.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n readonly userIdResolver?: ComposioUserIdResolver;\n\n private readonly apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n defaultScope: config.defaultScope,\n });\n this.apiKey = config.apiKey;\n this.userIdResolver = config.userIdResolver;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n // 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 const identity = await this.resolveExecutionIdentity(opts);\n const composio = this.getMastraClient();\n const sessionToolSlugs = opts.toolSlugs.filter(slug => COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const directToolSlugs = opts.toolSlugs.filter(slug => !COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const mastraTools: MastraToolCollection = {};\n\n if (directToolSlugs.length > 0) {\n const modifiers = {\n // `connectedAccountId` is not threaded through Composio's `execute`\n // option bag in @composio/mastra; the only documented per-call hook\n // is `beforeExecute`, which receives the params object that flows\n // into the API call. Mutating `params.connectedAccountId` routes\n // the call to a specific account.\n beforeExecute: ({ params }: { params: { connectedAccountId?: string; userId?: string } }) => {\n if (identity.connectionId) {\n params.connectedAccountId = identity.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(identity.userId, { tools: directToolSlugs }, modifiers)) as MastraToolCollection,\n );\n }\n\n if (sessionToolSlugs.length > 0) {\n const selectedToolkits = [\n ...new Set(\n Object.values(opts.toolMeta)\n .map(meta => meta.toolkit)\n .filter(\n (toolkit): toolkit is string =>\n typeof toolkit === 'string' && toolkit.toLowerCase() !== COMPOSIO_PROVIDER_ID,\n ),\n ),\n ];\n const session = await composio.sessions.create(identity.userId, {\n ...(selectedToolkits.length > 0 ? { toolkits: selectedToolkits } : {}),\n manageConnections: { enable: true, waitForConnections: true },\n sandbox: { enable: false },\n });\n const sessionTools = (await session.tools()) as MastraToolCollection;\n\n for (const slug of sessionToolSlugs) {\n const tool = sessionTools[slug];\n if (tool) mastraTools[slug] = tool;\n }\n }\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools)) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n /**\n * Run the configured `userIdResolver` and validate its result. Returns\n * the resolved user id, or `undefined` when no resolver is configured or\n * the resolver declined (returned `undefined`). Throws when the resolver\n * returns an empty or non-string value — an empty execution identity must\n * fail closed instead of silently falling back.\n */\n private async runUserIdResolver(input: ComposioUserIdResolverInput): Promise<string | undefined> {\n if (!this.userIdResolver) return undefined;\n const resolved = await this.userIdResolver(input);\n if (resolved === undefined) return undefined;\n if (typeof resolved !== 'string') {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n const normalized = resolved.trim();\n if (normalized.length === 0) {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n return normalized;\n }\n\n /**\n * Resolve the effective Composio execution identity for one\n * `resolveToolsVNext` call: the `userId` bucket to fetch tools under and\n * the exact `connectedAccountId` to route execution to (absent = let\n * Composio auto-resolve within the bucket).\n */\n private async resolveExecutionIdentity(opts: ResolveToolsOpts): Promise<{ userId: string; connectionId?: string }> {\n // The unpinned caller-supplied bootstrap fan-out passes the user bucket\n // itself as `connectionId` (connectionId === authorId). That is not an\n // account pin, so execution must stay on Composio's per-bucket\n // auto-resolve.\n const hasAccountPin = opts.connectionId !== opts.authorId;\n\n if (opts.kind === 'invoker') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: opts.connectionId,\n });\n // Invoker connections execute as the authenticated user — never the\n // Memory resource id — against the exact stored account pin (which may\n // be an account another user shared with the invoker via Composio ACL).\n return {\n userId: resolvedUserId ?? resolveInvokerUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n if (opts.scope === 'caller-supplied') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: hasAccountPin ? opts.connectionId : undefined,\n });\n return {\n userId: resolvedUserId ?? resolveInternalUserId(opts.requestContext),\n connectionId: hasAccountPin ? opts.connectionId : undefined,\n };\n }\n\n // Author-bound (and legacy) connections: the runtime fan-out passes the\n // agent author's id explicitly. Use it as the Composio user bucket so the\n // pin resolves for any invoker (not just the original author), and always\n // route execution to the pinned account.\n return {\n userId: opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. When it is\n // present we must use `connectedAccounts.initiate`, which accepts a\n // discriminated `{ authScheme, val }` config for programmatic account\n // creation. Composio's non-deprecated `connectedAccounts.link` (hosted\n // Connect Link) has no `config` parameter, so it cannot carry these fields.\n const initiateConfig =\n opts.config && Object.keys(opts.config).length > 0 && authScheme\n ? ({ authScheme, val: opts.config } as unknown as Parameters<\n typeof composio.connectedAccounts.initiate\n >[2] extends infer O\n ? O extends { config?: infer C }\n ? C\n : never\n : never)\n : undefined;\n\n // Prefer `link` for the Composio-managed OAuth redirect flow: `initiate`\n // is deprecated for managed OAuth. `link` allows multiple connected\n // accounts per (user, auth config) by default, so we no longer pass\n // `allowMultiple`. Fall back to `initiate` only when custom `config` fields\n // are supplied, since `link` cannot forward them.\n const request = initiateConfig\n ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n config: initiateConfig,\n })\n : await composio.connectedAccounts.link(internalUserId, authConfigId);\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] authorize did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n limit: perPage,\n });\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = (list.items ?? []).map(account => ({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n }));\n\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n const hasMore = typeof nextCursor === 'string' && nextCursor.length > 0;\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\nfunction readAuthenticatedUserId(requestContext?: RequestContext): string | undefined {\n const user = requestContext?.getRaw(MASTRA_USER_KEY);\n if (!user || typeof user !== 'object' || !('id' in user)) return undefined;\n return typeof user.id === 'string' && user.id.length > 0 ? user.id : undefined;\n}\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: RequestContext): string {\n const resourceId = requestContext?.getRaw(MASTRA_RESOURCE_ID_KEY);\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n return readAuthenticatedUserId(requestContext) ?? DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Read the authenticated invoker's Composio `userId` from per-request\n * context. Invoker connections must never fall back to the Memory resource id\n * because a project or thread is not an authenticated connector principal.\n */\nfunction resolveInvokerUserId(requestContext?: RequestContext): string {\n const userId = readAuthenticatedUserId(requestContext);\n if (userId) return userId;\n throw new Error('[composio] kind \"invoker\" requires an authenticated user or a userIdResolver result');\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"composio.cjs","names":["BaseToolProvider","Composio","MastraProvider","MASTRA_RESOURCE_ID_KEY"],"sources":["../src/providers/composio.ts"],"sourcesContent":["import type {\n AuthFlowStatus,\n AuthorizeOpts,\n ConnectionField,\n ExistingConnection,\n ListConnectionsOpts,\n ListConnectionsResult,\n ListToolsOpts,\n ListToolsResult,\n ResolveToolsOpts,\n ToolProviderCapabilities,\n ToolProviderHealth,\n ToolProviderInfo,\n ToolProviderToolkit,\n BaseToolProviderOptions,\n} from '@mastra/core/tool-provider';\nimport { BaseToolProvider } from '@mastra/core/tool-provider';\nimport type { ToolAction } from '@mastra/core/tools';\nimport { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n /**\n * Server-side resolver mapping request context to the Composio `userId` the\n * call should execute as. Runs for `kind: 'invoker'` and `caller-supplied`\n * resolution, so the host application (for example, its FGA layer) can\n * derive and authorize the effective user before execution.\n *\n * Only server-populated fields within request context are trusted. When the\n * resolver is absent (or returns `undefined`), invoker connections require\n * the authenticated user (`MASTRA_USER_KEY`). Legacy `caller-supplied`\n * connections retain their existing resource-id fallback.\n *\n * The exact `connectedAccountId` always comes from the stored connection\n * pin — the resolver cannot override it.\n */\n userIdResolver?: ComposioUserIdResolver;\n}\n\n/** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */\nexport interface ComposioUserIdResolverInput {\n /** Live per-request context. Use `get()` for declared keys and `getRaw()` for reserved runtime keys. */\n requestContext?: RequestContext;\n /** Toolkit slug the identity is being resolved for, when known. */\n toolkit?: string;\n /**\n * The stored connection pin being resolved, when one exists. Hosts can use\n * it to validate that the invoker is allowed to use this exact account.\n */\n connectedAccountId?: string;\n}\n\n/**\n * Server-side resolver returning the effective Composio `userId` for a\n * request. Returning `undefined` falls back to the provider's default\n * identity resolution. Must never trust client-supplied context values.\n */\nexport type ComposioUserIdResolver = (\n input: ComposioUserIdResolverInput,\n) => Promise<string | undefined> | string | undefined;\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\nconst COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = new Set(['COMPOSIO_MANAGE_CONNECTIONS', 'COMPOSIO_WAIT_FOR_CONNECTIONS']);\n\n/**\n * Composio implementation of the {@link BaseToolProvider} contract.\n *\n * Discovery (`listAllToolkits`, `listAllTools`) uses the raw Composio\n * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved\n * tools are already in `createTool()` shape. Ordinary tools use Composio's\n * direct-tools API, while connection-management tools use a caller-scoped\n * Tool Router session. Resolved tools keep the `outputSchema` supplied by\n * `@composio/mastra`, which pre-relaxes Composio's strict API schemas\n * (nullable fields, extra properties, no `required`) so real third-party\n * responses validate while structurally invalid output is still rejected.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n readonly userIdResolver?: ComposioUserIdResolver;\n\n private readonly apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n defaultScope: config.defaultScope,\n });\n this.apiKey = config.apiKey;\n this.userIdResolver = config.userIdResolver;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n const rawTools: ComposioTool[] = await composio.tools.getRawComposioTools(query);\n\n const data = rawTools.map(tool => ({\n slug: tool.slug,\n name: tool.name ?? tool.slug,\n description: tool.description,\n toolkit: tool.toolkit?.slug ?? opts.toolkit ?? '',\n }));\n\n return {\n data,\n pagination: {\n page: opts.page ?? 1,\n perPage: limit,\n hasMore: limit !== undefined && rawTools.length >= limit,\n },\n };\n }\n\n // ── runtime ───────────────────────────────────────────────────────────\n\n async resolveToolsVNext(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>> {\n if (opts.toolSlugs.length === 0) return {};\n\n const identity = await this.resolveExecutionIdentity(opts);\n const composio = this.getMastraClient();\n const sessionToolSlugs = opts.toolSlugs.filter(slug => COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const directToolSlugs = opts.toolSlugs.filter(slug => !COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const mastraTools: MastraToolCollection = {};\n\n if (directToolSlugs.length > 0) {\n const modifiers = {\n // `connectedAccountId` is not threaded through Composio's `execute`\n // option bag in @composio/mastra; the only documented per-call hook\n // is `beforeExecute`, which receives the params object that flows\n // into the API call. Mutating `params.connectedAccountId` routes\n // the call to a specific account.\n beforeExecute: ({ params }: { params: { connectedAccountId?: string; userId?: string } }) => {\n if (identity.connectionId) {\n params.connectedAccountId = identity.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(identity.userId, { tools: directToolSlugs }, modifiers)) as MastraToolCollection,\n );\n }\n\n if (sessionToolSlugs.length > 0) {\n const selectedToolkits = [\n ...new Set(\n Object.values(opts.toolMeta)\n .map(meta => meta.toolkit)\n .filter(\n (toolkit): toolkit is string =>\n typeof toolkit === 'string' && toolkit.toLowerCase() !== COMPOSIO_PROVIDER_ID,\n ),\n ),\n ];\n const session = await composio.sessions.create(identity.userId, {\n ...(selectedToolkits.length > 0 ? { toolkits: selectedToolkits } : {}),\n manageConnections: { enable: true, waitForConnections: true },\n sandbox: { enable: false },\n });\n const sessionTools = (await session.tools()) as MastraToolCollection;\n\n for (const slug of sessionToolSlugs) {\n const tool = sessionTools[slug];\n if (tool) mastraTools[slug] = tool;\n }\n }\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools)) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n /**\n * Run the configured `userIdResolver` and validate its result. Returns\n * the resolved user id, or `undefined` when no resolver is configured or\n * the resolver declined (returned `undefined`). Throws when the resolver\n * returns an empty or non-string value — an empty execution identity must\n * fail closed instead of silently falling back.\n */\n private async runUserIdResolver(input: ComposioUserIdResolverInput): Promise<string | undefined> {\n if (!this.userIdResolver) return undefined;\n const resolved = await this.userIdResolver(input);\n if (resolved === undefined) return undefined;\n if (typeof resolved !== 'string') {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n const normalized = resolved.trim();\n if (normalized.length === 0) {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n return normalized;\n }\n\n /**\n * Resolve the effective Composio execution identity for one\n * `resolveToolsVNext` call: the `userId` bucket to fetch tools under and\n * the exact `connectedAccountId` to route execution to (absent = let\n * Composio auto-resolve within the bucket).\n */\n private async resolveExecutionIdentity(opts: ResolveToolsOpts): Promise<{ userId: string; connectionId?: string }> {\n // The unpinned caller-supplied bootstrap fan-out passes the user bucket\n // itself as `connectionId` (connectionId === authorId). That is not an\n // account pin, so execution must stay on Composio's per-bucket\n // auto-resolve.\n const hasAccountPin = opts.connectionId !== opts.authorId;\n\n if (opts.kind === 'invoker') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: opts.connectionId,\n });\n // Invoker connections execute as the authenticated user — never the\n // Memory resource id — against the exact stored account pin (which may\n // be an account another user shared with the invoker via Composio ACL).\n return {\n userId: resolvedUserId ?? resolveInvokerUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n if (opts.scope === 'caller-supplied') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: hasAccountPin ? opts.connectionId : undefined,\n });\n return {\n userId: resolvedUserId ?? resolveInternalUserId(opts.requestContext),\n connectionId: hasAccountPin ? opts.connectionId : undefined,\n };\n }\n\n // Author-bound (and legacy) connections: the runtime fan-out passes the\n // agent author's id explicitly. Use it as the Composio user bucket so the\n // pin resolves for any invoker (not just the original author), and always\n // route execution to the pinned account.\n return {\n userId: opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. When it is\n // present we must use `connectedAccounts.initiate`, which accepts a\n // discriminated `{ authScheme, val }` config for programmatic account\n // creation. Composio's non-deprecated `connectedAccounts.link` (hosted\n // Connect Link) has no `config` parameter, so it cannot carry these fields.\n const initiateConfig =\n opts.config && Object.keys(opts.config).length > 0 && authScheme\n ? ({ authScheme, val: opts.config } as unknown as Parameters<\n typeof composio.connectedAccounts.initiate\n >[2] extends infer O\n ? O extends { config?: infer C }\n ? C\n : never\n : never)\n : undefined;\n\n // Prefer `link` for the Composio-managed OAuth redirect flow: `initiate`\n // is deprecated for managed OAuth. `link` allows multiple connected\n // accounts per (user, auth config) by default, so we no longer pass\n // `allowMultiple`. Fall back to `initiate` only when custom `config` fields\n // are supplied, since `link` cannot forward them.\n const request = initiateConfig\n ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n config: initiateConfig,\n })\n : await composio.connectedAccounts.link(internalUserId, authConfigId);\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] authorize did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n limit: perPage,\n });\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = (list.items ?? []).map(account => ({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n }));\n\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n const hasMore = typeof nextCursor === 'string' && nextCursor.length > 0;\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\nfunction readAuthenticatedUserId(requestContext?: RequestContext): string | undefined {\n const user = requestContext?.getRaw(MASTRA_USER_KEY);\n if (!user || typeof user !== 'object' || !('id' in user)) return undefined;\n return typeof user.id === 'string' && user.id.length > 0 ? user.id : undefined;\n}\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: RequestContext): string {\n const resourceId = requestContext?.getRaw(MASTRA_RESOURCE_ID_KEY);\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n return readAuthenticatedUserId(requestContext) ?? DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Read the authenticated invoker's Composio `userId` from per-request\n * context. Invoker connections must never fall back to the Memory resource id\n * because a project or thread is not an authenticated connector principal.\n */\nfunction resolveInvokerUserId(requestContext?: RequestContext): string {\n const userId = readAuthenticatedUserId(requestContext);\n if (userId) return userId;\n throw new Error('[composio] kind \"invoker\" requires an authenticated user or a userIdResolver result');\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"composio.d.cts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;UAIF;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;UACT;UACA;EAER,YAAY,QAAQ;UAYZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EAuDrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6CvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EAyBvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EAgDpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
1
+ {"version":3,"file":"composio.d.cts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;UAIF;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;UACT;UACA;EAER,YAAY,QAAQ;UAYZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EA2CrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6CvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EAyBvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EAgDpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
@@ -1 +1 @@
1
- {"version":3,"file":"composio.d.ts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;UAIF;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;UACT;UACA;EAER,YAAY,QAAQ;UAYZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EAuDrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6CvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EAyBvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EAgDpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
1
+ {"version":3,"file":"composio.d.ts","names":[],"sources":["../src/providers/composio.ts"],"mappings":";;;;UA+BiB,mCAAmC;;EAElD;;;;;;;;;;;;;;;EAeA,iBAAiB;;;UAIF;;EAEf,iBAAiB;;EAEjB;;;;;EAKA;;;;;;;KAQU,0BACV,OAAO,gCACJ;;;;;;;;;;;;;;;;cAqBQ,6BAA6B;WAC/B,MAAM;WAKN,cAAc;WAOd,iBAAiB;mBAET;UACT;UACA;EAER,YAAY,QAAQ;UAYZ;UAOA;YAYQ,mBAAmB,QAAQ;YAW3B,aAAa,MAAM,gBAAgB,QAAQ;EA2CrD,kBAAkB,MAAM,mBAAmB,QAAQ,eAAe;;;;;;;;UAkF1D;;;;;;;UAoBA;EA8CR,UAAU,MAAM,gBAAgB;IAAU;IAAa;;EA6CvD,uBAAuB;IAAa;MAAoB,QAAQ;EAqBhE,cAAc,iBAAiB,QAAQ;EAkBvC,oBAAoB;IACxB,OAAO;MAAQ;MAAsB;;MACnC,QAAQ;IAAiB;;EAyBvB,gBAAgB,MAAM,sBAAsB,QAAQ;;;;;;;;;;;EAgDpD,iBAAiB,uBAAuB;EAaxC,aAAa,QAAQ;;;;;;UAoBb"}
package/dist/composio.js CHANGED
@@ -82,12 +82,7 @@ var ComposioToolProvider = class extends BaseToolProvider {
82
82
  toolkits: [],
83
83
  limit
84
84
  };
85
- let rawTools = [];
86
- try {
87
- rawTools = await composio.tools.getRawComposioTools(query);
88
- } catch (err) {
89
- console.warn(`[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`, err);
90
- }
85
+ const rawTools = await composio.tools.getRawComposioTools(query);
91
86
  return {
92
87
  data: rawTools.map((tool) => ({
93
88
  slug: tool.slug,
@@ -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';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n /**\n * Server-side resolver mapping request context to the Composio `userId` the\n * call should execute as. Runs for `kind: 'invoker'` and `caller-supplied`\n * resolution, so the host application (for example, its FGA layer) can\n * derive and authorize the effective user before execution.\n *\n * Only server-populated fields within request context are trusted. When the\n * resolver is absent (or returns `undefined`), invoker connections require\n * the authenticated user (`MASTRA_USER_KEY`). Legacy `caller-supplied`\n * connections retain their existing resource-id fallback.\n *\n * The exact `connectedAccountId` always comes from the stored connection\n * pin — the resolver cannot override it.\n */\n userIdResolver?: ComposioUserIdResolver;\n}\n\n/** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */\nexport interface ComposioUserIdResolverInput {\n /** Live per-request context. Use `get()` for declared keys and `getRaw()` for reserved runtime keys. */\n requestContext?: RequestContext;\n /** Toolkit slug the identity is being resolved for, when known. */\n toolkit?: string;\n /**\n * The stored connection pin being resolved, when one exists. Hosts can use\n * it to validate that the invoker is allowed to use this exact account.\n */\n connectedAccountId?: string;\n}\n\n/**\n * Server-side resolver returning the effective Composio `userId` for a\n * request. Returning `undefined` falls back to the provider's default\n * identity resolution. Must never trust client-supplied context values.\n */\nexport type ComposioUserIdResolver = (\n input: ComposioUserIdResolverInput,\n) => Promise<string | undefined> | string | undefined;\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\nconst COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = new Set(['COMPOSIO_MANAGE_CONNECTIONS', 'COMPOSIO_WAIT_FOR_CONNECTIONS']);\n\n/**\n * Composio implementation of the {@link BaseToolProvider} contract.\n *\n * Discovery (`listAllToolkits`, `listAllTools`) uses the raw Composio\n * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved\n * tools are already in `createTool()` shape. Ordinary tools use Composio's\n * direct-tools API, while connection-management tools use a caller-scoped\n * Tool Router session. Resolved tools keep the `outputSchema` supplied by\n * `@composio/mastra`, which pre-relaxes Composio's strict API schemas\n * (nullable fields, extra properties, no `required`) so real third-party\n * responses validate while structurally invalid output is still rejected.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n readonly userIdResolver?: ComposioUserIdResolver;\n\n private readonly apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n defaultScope: config.defaultScope,\n });\n this.apiKey = config.apiKey;\n this.userIdResolver = config.userIdResolver;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n // 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 const identity = await this.resolveExecutionIdentity(opts);\n const composio = this.getMastraClient();\n const sessionToolSlugs = opts.toolSlugs.filter(slug => COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const directToolSlugs = opts.toolSlugs.filter(slug => !COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const mastraTools: MastraToolCollection = {};\n\n if (directToolSlugs.length > 0) {\n const modifiers = {\n // `connectedAccountId` is not threaded through Composio's `execute`\n // option bag in @composio/mastra; the only documented per-call hook\n // is `beforeExecute`, which receives the params object that flows\n // into the API call. Mutating `params.connectedAccountId` routes\n // the call to a specific account.\n beforeExecute: ({ params }: { params: { connectedAccountId?: string; userId?: string } }) => {\n if (identity.connectionId) {\n params.connectedAccountId = identity.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(identity.userId, { tools: directToolSlugs }, modifiers)) as MastraToolCollection,\n );\n }\n\n if (sessionToolSlugs.length > 0) {\n const selectedToolkits = [\n ...new Set(\n Object.values(opts.toolMeta)\n .map(meta => meta.toolkit)\n .filter(\n (toolkit): toolkit is string =>\n typeof toolkit === 'string' && toolkit.toLowerCase() !== COMPOSIO_PROVIDER_ID,\n ),\n ),\n ];\n const session = await composio.sessions.create(identity.userId, {\n ...(selectedToolkits.length > 0 ? { toolkits: selectedToolkits } : {}),\n manageConnections: { enable: true, waitForConnections: true },\n sandbox: { enable: false },\n });\n const sessionTools = (await session.tools()) as MastraToolCollection;\n\n for (const slug of sessionToolSlugs) {\n const tool = sessionTools[slug];\n if (tool) mastraTools[slug] = tool;\n }\n }\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools)) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n /**\n * Run the configured `userIdResolver` and validate its result. Returns\n * the resolved user id, or `undefined` when no resolver is configured or\n * the resolver declined (returned `undefined`). Throws when the resolver\n * returns an empty or non-string value — an empty execution identity must\n * fail closed instead of silently falling back.\n */\n private async runUserIdResolver(input: ComposioUserIdResolverInput): Promise<string | undefined> {\n if (!this.userIdResolver) return undefined;\n const resolved = await this.userIdResolver(input);\n if (resolved === undefined) return undefined;\n if (typeof resolved !== 'string') {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n const normalized = resolved.trim();\n if (normalized.length === 0) {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n return normalized;\n }\n\n /**\n * Resolve the effective Composio execution identity for one\n * `resolveToolsVNext` call: the `userId` bucket to fetch tools under and\n * the exact `connectedAccountId` to route execution to (absent = let\n * Composio auto-resolve within the bucket).\n */\n private async resolveExecutionIdentity(opts: ResolveToolsOpts): Promise<{ userId: string; connectionId?: string }> {\n // The unpinned caller-supplied bootstrap fan-out passes the user bucket\n // itself as `connectionId` (connectionId === authorId). That is not an\n // account pin, so execution must stay on Composio's per-bucket\n // auto-resolve.\n const hasAccountPin = opts.connectionId !== opts.authorId;\n\n if (opts.kind === 'invoker') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: opts.connectionId,\n });\n // Invoker connections execute as the authenticated user — never the\n // Memory resource id — against the exact stored account pin (which may\n // be an account another user shared with the invoker via Composio ACL).\n return {\n userId: resolvedUserId ?? resolveInvokerUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n if (opts.scope === 'caller-supplied') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: hasAccountPin ? opts.connectionId : undefined,\n });\n return {\n userId: resolvedUserId ?? resolveInternalUserId(opts.requestContext),\n connectionId: hasAccountPin ? opts.connectionId : undefined,\n };\n }\n\n // Author-bound (and legacy) connections: the runtime fan-out passes the\n // agent author's id explicitly. Use it as the Composio user bucket so the\n // pin resolves for any invoker (not just the original author), and always\n // route execution to the pinned account.\n return {\n userId: opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. When it is\n // present we must use `connectedAccounts.initiate`, which accepts a\n // discriminated `{ authScheme, val }` config for programmatic account\n // creation. Composio's non-deprecated `connectedAccounts.link` (hosted\n // Connect Link) has no `config` parameter, so it cannot carry these fields.\n const initiateConfig =\n opts.config && Object.keys(opts.config).length > 0 && authScheme\n ? ({ authScheme, val: opts.config } as unknown as Parameters<\n typeof composio.connectedAccounts.initiate\n >[2] extends infer O\n ? O extends { config?: infer C }\n ? C\n : never\n : never)\n : undefined;\n\n // Prefer `link` for the Composio-managed OAuth redirect flow: `initiate`\n // is deprecated for managed OAuth. `link` allows multiple connected\n // accounts per (user, auth config) by default, so we no longer pass\n // `allowMultiple`. Fall back to `initiate` only when custom `config` fields\n // are supplied, since `link` cannot forward them.\n const request = initiateConfig\n ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n config: initiateConfig,\n })\n : await composio.connectedAccounts.link(internalUserId, authConfigId);\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] authorize did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n limit: perPage,\n });\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = (list.items ?? []).map(account => ({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n }));\n\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n const hasMore = typeof nextCursor === 'string' && nextCursor.length > 0;\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\nfunction readAuthenticatedUserId(requestContext?: RequestContext): string | undefined {\n const user = requestContext?.getRaw(MASTRA_USER_KEY);\n if (!user || typeof user !== 'object' || !('id' in user)) return undefined;\n return typeof user.id === 'string' && user.id.length > 0 ? user.id : undefined;\n}\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: RequestContext): string {\n const resourceId = requestContext?.getRaw(MASTRA_RESOURCE_ID_KEY);\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n return readAuthenticatedUserId(requestContext) ?? DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Read the authenticated invoker's Composio `userId` from per-request\n * context. Invoker connections must never fall back to the Memory resource id\n * because a project or thread is not an authenticated connector principal.\n */\nfunction resolveInvokerUserId(requestContext?: RequestContext): string {\n const userId = readAuthenticatedUserId(requestContext);\n if (userId) return userId;\n throw new Error('[composio] kind \"invoker\" requires an authenticated user or a userIdResolver result');\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"composio.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';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n /**\n * Server-side resolver mapping request context to the Composio `userId` the\n * call should execute as. Runs for `kind: 'invoker'` and `caller-supplied`\n * resolution, so the host application (for example, its FGA layer) can\n * derive and authorize the effective user before execution.\n *\n * Only server-populated fields within request context are trusted. When the\n * resolver is absent (or returns `undefined`), invoker connections require\n * the authenticated user (`MASTRA_USER_KEY`). Legacy `caller-supplied`\n * connections retain their existing resource-id fallback.\n *\n * The exact `connectedAccountId` always comes from the stored connection\n * pin — the resolver cannot override it.\n */\n userIdResolver?: ComposioUserIdResolver;\n}\n\n/** Inputs handed to {@link ComposioToolProviderConfig.userIdResolver}. */\nexport interface ComposioUserIdResolverInput {\n /** Live per-request context. Use `get()` for declared keys and `getRaw()` for reserved runtime keys. */\n requestContext?: RequestContext;\n /** Toolkit slug the identity is being resolved for, when known. */\n toolkit?: string;\n /**\n * The stored connection pin being resolved, when one exists. Hosts can use\n * it to validate that the invoker is allowed to use this exact account.\n */\n connectedAccountId?: string;\n}\n\n/**\n * Server-side resolver returning the effective Composio `userId` for a\n * request. Returning `undefined` falls back to the provider's default\n * identity resolution. Must never trust client-supplied context values.\n */\nexport type ComposioUserIdResolver = (\n input: ComposioUserIdResolverInput,\n) => Promise<string | undefined> | string | undefined;\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\nconst COMPOSIO_CONNECTION_MANAGEMENT_TOOLS = new Set(['COMPOSIO_MANAGE_CONNECTIONS', 'COMPOSIO_WAIT_FOR_CONNECTIONS']);\n\n/**\n * Composio implementation of the {@link BaseToolProvider} contract.\n *\n * Discovery (`listAllToolkits`, `listAllTools`) uses the raw Composio\n * client. Runtime (`resolveToolsVNext`) uses {@link MastraProvider} so resolved\n * tools are already in `createTool()` shape. Ordinary tools use Composio's\n * direct-tools API, while connection-management tools use a caller-scoped\n * Tool Router session. Resolved tools keep the `outputSchema` supplied by\n * `@composio/mastra`, which pre-relaxes Composio's strict API schemas\n * (nullable fields, extra properties, no `required`) so real third-party\n * responses validate while structurally invalid output is still rejected.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n readonly userIdResolver?: ComposioUserIdResolver;\n\n private readonly apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n defaultScope: config.defaultScope,\n });\n this.apiKey = config.apiKey;\n this.userIdResolver = config.userIdResolver;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n const rawTools: ComposioTool[] = await composio.tools.getRawComposioTools(query);\n\n const data = rawTools.map(tool => ({\n slug: tool.slug,\n name: tool.name ?? tool.slug,\n description: tool.description,\n toolkit: tool.toolkit?.slug ?? opts.toolkit ?? '',\n }));\n\n return {\n data,\n pagination: {\n page: opts.page ?? 1,\n perPage: limit,\n hasMore: limit !== undefined && rawTools.length >= limit,\n },\n };\n }\n\n // ── runtime ───────────────────────────────────────────────────────────\n\n async resolveToolsVNext(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>> {\n if (opts.toolSlugs.length === 0) return {};\n\n const identity = await this.resolveExecutionIdentity(opts);\n const composio = this.getMastraClient();\n const sessionToolSlugs = opts.toolSlugs.filter(slug => COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const directToolSlugs = opts.toolSlugs.filter(slug => !COMPOSIO_CONNECTION_MANAGEMENT_TOOLS.has(slug));\n const mastraTools: MastraToolCollection = {};\n\n if (directToolSlugs.length > 0) {\n const modifiers = {\n // `connectedAccountId` is not threaded through Composio's `execute`\n // option bag in @composio/mastra; the only documented per-call hook\n // is `beforeExecute`, which receives the params object that flows\n // into the API call. Mutating `params.connectedAccountId` routes\n // the call to a specific account.\n beforeExecute: ({ params }: { params: { connectedAccountId?: string; userId?: string } }) => {\n if (identity.connectionId) {\n params.connectedAccountId = identity.connectionId;\n }\n return params;\n },\n };\n\n Object.assign(\n mastraTools,\n (await composio.tools.get(identity.userId, { tools: directToolSlugs }, modifiers)) as MastraToolCollection,\n );\n }\n\n if (sessionToolSlugs.length > 0) {\n const selectedToolkits = [\n ...new Set(\n Object.values(opts.toolMeta)\n .map(meta => meta.toolkit)\n .filter(\n (toolkit): toolkit is string =>\n typeof toolkit === 'string' && toolkit.toLowerCase() !== COMPOSIO_PROVIDER_ID,\n ),\n ),\n ];\n const session = await composio.sessions.create(identity.userId, {\n ...(selectedToolkits.length > 0 ? { toolkits: selectedToolkits } : {}),\n manageConnections: { enable: true, waitForConnections: true },\n sandbox: { enable: false },\n });\n const sessionTools = (await session.tools()) as MastraToolCollection;\n\n for (const slug of sessionToolSlugs) {\n const tool = sessionTools[slug];\n if (tool) mastraTools[slug] = tool;\n }\n }\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools)) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n /**\n * Run the configured `userIdResolver` and validate its result. Returns\n * the resolved user id, or `undefined` when no resolver is configured or\n * the resolver declined (returned `undefined`). Throws when the resolver\n * returns an empty or non-string value — an empty execution identity must\n * fail closed instead of silently falling back.\n */\n private async runUserIdResolver(input: ComposioUserIdResolverInput): Promise<string | undefined> {\n if (!this.userIdResolver) return undefined;\n const resolved = await this.userIdResolver(input);\n if (resolved === undefined) return undefined;\n if (typeof resolved !== 'string') {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n const normalized = resolved.trim();\n if (normalized.length === 0) {\n throw new Error('[composio] userIdResolver must return a non-empty string or undefined');\n }\n return normalized;\n }\n\n /**\n * Resolve the effective Composio execution identity for one\n * `resolveToolsVNext` call: the `userId` bucket to fetch tools under and\n * the exact `connectedAccountId` to route execution to (absent = let\n * Composio auto-resolve within the bucket).\n */\n private async resolveExecutionIdentity(opts: ResolveToolsOpts): Promise<{ userId: string; connectionId?: string }> {\n // The unpinned caller-supplied bootstrap fan-out passes the user bucket\n // itself as `connectionId` (connectionId === authorId). That is not an\n // account pin, so execution must stay on Composio's per-bucket\n // auto-resolve.\n const hasAccountPin = opts.connectionId !== opts.authorId;\n\n if (opts.kind === 'invoker') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: opts.connectionId,\n });\n // Invoker connections execute as the authenticated user — never the\n // Memory resource id — against the exact stored account pin (which may\n // be an account another user shared with the invoker via Composio ACL).\n return {\n userId: resolvedUserId ?? resolveInvokerUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n if (opts.scope === 'caller-supplied') {\n const resolvedUserId = await this.runUserIdResolver({\n requestContext: opts.requestContext,\n toolkit: opts.toolkit,\n connectedAccountId: hasAccountPin ? opts.connectionId : undefined,\n });\n return {\n userId: resolvedUserId ?? resolveInternalUserId(opts.requestContext),\n connectionId: hasAccountPin ? opts.connectionId : undefined,\n };\n }\n\n // Author-bound (and legacy) connections: the runtime fan-out passes the\n // agent author's id explicitly. Use it as the Composio user bucket so the\n // pin resolves for any invoker (not just the original author), and always\n // route execution to the pinned account.\n return {\n userId: opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext),\n connectionId: opts.connectionId,\n };\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. When it is\n // present we must use `connectedAccounts.initiate`, which accepts a\n // discriminated `{ authScheme, val }` config for programmatic account\n // creation. Composio's non-deprecated `connectedAccounts.link` (hosted\n // Connect Link) has no `config` parameter, so it cannot carry these fields.\n const initiateConfig =\n opts.config && Object.keys(opts.config).length > 0 && authScheme\n ? ({ authScheme, val: opts.config } as unknown as Parameters<\n typeof composio.connectedAccounts.initiate\n >[2] extends infer O\n ? O extends { config?: infer C }\n ? C\n : never\n : never)\n : undefined;\n\n // Prefer `link` for the Composio-managed OAuth redirect flow: `initiate`\n // is deprecated for managed OAuth. `link` allows multiple connected\n // accounts per (user, auth config) by default, so we no longer pass\n // `allowMultiple`. Fall back to `initiate` only when custom `config` fields\n // are supplied, since `link` cannot forward them.\n const request = initiateConfig\n ? await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n config: initiateConfig,\n })\n : await composio.connectedAccounts.link(internalUserId, authConfigId);\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] authorize did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n limit: perPage,\n });\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = (list.items ?? []).map(account => ({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n }));\n\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n const hasMore = typeof nextCursor === 'string' && nextCursor.length > 0;\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\nfunction readAuthenticatedUserId(requestContext?: RequestContext): string | undefined {\n const user = requestContext?.getRaw(MASTRA_USER_KEY);\n if (!user || typeof user !== 'object' || !('id' in user)) return undefined;\n return typeof user.id === 'string' && user.id.length > 0 ? user.id : undefined;\n}\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: RequestContext): string {\n const resourceId = requestContext?.getRaw(MASTRA_RESOURCE_ID_KEY);\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n return readAuthenticatedUserId(requestContext) ?? DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Read the authenticated invoker's Composio `userId` from per-request\n * context. Invoker connections must never fall back to the Memory resource id\n * because a project or thread is not an authenticated connector principal.\n */\nfunction resolveInvokerUserId(requestContext?: RequestContext): string {\n const userId = readAuthenticatedUserId(requestContext);\n if (userId) return userId;\n throw new Error('[composio] kind \"invoker\" requires an authenticated user or a userIdResolver result');\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -2,7 +2,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _mastra_core_processor_provider = require("@mastra/core/processor-provider");
3
3
  let _mastra_core_storage = require("@mastra/core/storage");
4
4
  let _mastra_core_tool_provider = require("@mastra/core/tool-provider");
5
- let crypto$1 = require("crypto");
6
5
  let _mastra_memory = require("@mastra/memory");
7
6
  let _mastra_core_agent = require("@mastra/core/agent");
8
7
  let _mastra_core_workspace = require("@mastra/core/workspace");
@@ -11,6 +10,7 @@ let _mastra_core_request_context = require("@mastra/core/request-context");
11
10
  let _mastra_core_processors = require("@mastra/core/processors");
12
11
  let _mastra_core_workflows = require("@mastra/core/workflows");
13
12
  let _mastra_core_utils = require("@mastra/core/utils");
13
+ let crypto$1 = require("crypto");
14
14
  let _mastra_core_evals = require("@mastra/core/evals");
15
15
  //#region src/namespaces/base.ts
16
16
  /**
@@ -299,6 +299,25 @@ async function resolveInstructionBlocks(blocks, context, deps) {
299
299
  //#region src/processor-graph-hydrator.ts
300
300
  const PASSTHROUGH_STEP_PREFIX = "passthrough-";
301
301
  /**
302
+ * Override a hydrated workflow step's ID so it derives from the unique
303
+ * `ProcessorGraphStep.id` of the graph node (`processor:<graphStepId>`) rather
304
+ * than from the resolved processor's stable `id`. Without this, two distinct
305
+ * graph nodes whose provider returns processors with the same `id` collapse to a
306
+ * single workflow step (steps are keyed by `step.id`), overwriting configured
307
+ * nodes and making parallel/branch outputs ambiguous.
308
+ *
309
+ * Uses a shallow spread (not core's `cloneStep`) so that every property is
310
+ * preserved — notably `providesSkillDiscovery` and the bound
311
+ * `getLoadedToolsForRequestContext`, which `cloneStep` drops — and so the
312
+ * provider-created processor is never mutated.
313
+ */
314
+ function withGraphStepId(step, graphStepId) {
315
+ return {
316
+ ...step,
317
+ id: `processor:${graphStepId}`
318
+ };
319
+ }
320
+ /**
302
321
  * Resolve a single ProcessorGraphStep into a PhaseFilteredProcessor instance.
303
322
  */
304
323
  function resolveStep(step, ctx) {
@@ -382,7 +401,7 @@ function buildWorkflow(entries, workflowId, ctx) {
382
401
  for (const entry of entries) if (entry.type === "step") {
383
402
  const processor = resolveStep(entry.step, ctx);
384
403
  if (!processor) continue;
385
- const step = (0, _mastra_core_workflows.createStep)(processor);
404
+ const step = withGraphStepId((0, _mastra_core_workflows.createStep)(processor), entry.step.id);
386
405
  workflow = workflow.then(step);
387
406
  hasSteps = true;
388
407
  } else if (entry.type === "parallel") {
@@ -390,7 +409,7 @@ function buildWorkflow(entries, workflowId, ctx) {
390
409
  if (branchEntries.length === 1 && branchEntries[0].type === "step") {
391
410
  const proc = resolveStep(branchEntries[0].step, ctx);
392
411
  if (!proc) return void 0;
393
- return (0, _mastra_core_workflows.createStep)(proc);
412
+ return withGraphStepId((0, _mastra_core_workflows.createStep)(proc), branchEntries[0].step.id);
394
413
  }
395
414
  return buildWorkflow(branchEntries, `${workflowId}-parallel-branch-${branchIdx}`, ctx);
396
415
  }).filter((s) => Boolean(s));
@@ -401,23 +420,30 @@ function buildWorkflow(entries, workflowId, ctx) {
401
420
  }
402
421
  } else if (entry.type === "conditional") {
403
422
  const branchTuples = [];
423
+ const explicitRuleGroups = [];
424
+ let hasUserDefault = false;
425
+ const anyExplicitMatch = (inputData) => explicitRuleGroups.some((rules) => evaluateRuleGroup(rules, inputData));
404
426
  for (const [i, condition] of entry.conditions.entries()) {
405
427
  let branchStep;
406
428
  if (condition.steps.length === 1 && condition.steps[0].type === "step") {
407
429
  const proc = resolveStep(condition.steps[0].step, ctx);
408
430
  if (!proc) continue;
409
- branchStep = (0, _mastra_core_workflows.createStep)(proc);
431
+ branchStep = withGraphStepId((0, _mastra_core_workflows.createStep)(proc), condition.steps[0].step.id);
410
432
  } else {
411
433
  branchStep = buildWorkflow(condition.steps, `${workflowId}-cond-branch-${i}`, ctx);
412
434
  if (!branchStep) continue;
413
435
  }
414
436
  if (condition.rules) {
415
437
  const rules = condition.rules;
438
+ explicitRuleGroups.push(rules);
416
439
  const conditionFn = async ({ inputData }) => {
417
440
  return evaluateRuleGroup(rules, inputData);
418
441
  };
419
442
  branchTuples.push([conditionFn, branchStep]);
420
- } else branchTuples.push([async () => true, branchStep]);
443
+ } else {
444
+ hasUserDefault = true;
445
+ branchTuples.push([async ({ inputData }) => !anyExplicitMatch(inputData), branchStep]);
446
+ }
421
447
  }
422
448
  if (branchTuples.length > 0) {
423
449
  const passthroughStep = (0, _mastra_core_workflows.createStep)({
@@ -426,7 +452,7 @@ function buildWorkflow(entries, workflowId, ctx) {
426
452
  outputSchema: _mastra_core_processors.ProcessorStepSchema,
427
453
  execute: async ({ inputData }) => inputData
428
454
  });
429
- branchTuples.push([async () => true, passthroughStep]);
455
+ branchTuples.push([async ({ inputData }) => !anyExplicitMatch(inputData) && !hasUserDefault, passthroughStep]);
430
456
  workflow = workflow.branch(branchTuples);
431
457
  workflow = workflow.map(mergeBranchOutputs);
432
458
  hasSteps = true;
@@ -588,6 +614,42 @@ async function createVersionFromSnapshotUpdate({ store, parentId, parentIdField,
588
614
  throw lastError;
589
615
  }
590
616
  //#endregion
617
+ //#region src/namespaces/workspace-identity.ts
618
+ /**
619
+ * `JSON.stringify` with deterministic key ordering at every level.
620
+ *
621
+ * Object key order is preserved by `JSON.stringify`, so semantically identical
622
+ * configs whose keys were inserted in a different order (e.g. after an agent
623
+ * update, an import, or a storage backend that normalizes key order) would
624
+ * otherwise serialize differently. Sorting keys recursively makes the output
625
+ * canonical. Array order and scalar values are left untouched, so they remain
626
+ * significant.
627
+ */
628
+ function stableStringify(value) {
629
+ return JSON.stringify(value, (_key, val) => {
630
+ if (val && typeof val === "object" && !Array.isArray(val)) {
631
+ const sorted = Object.create(null);
632
+ for (const k of Object.keys(val).sort()) sorted[k] = val[k];
633
+ return sorted;
634
+ }
635
+ return val;
636
+ });
637
+ }
638
+ /**
639
+ * Derive a deterministic identity for an inline workspace config.
640
+ *
641
+ * The ID is content-addressed via a canonical (key-order-independent) hash so
642
+ * that equivalent configs resolve to the same stored workspace instead of
643
+ * creating duplicates. Array order and value differences remain significant.
644
+ */
645
+ function computeInlineWorkspaceIdentity(config) {
646
+ const configHash = (0, crypto$1.createHash)("sha256").update(stableStringify(config)).digest("hex").slice(0, 12);
647
+ return {
648
+ workspaceId: `inline-${configHash}`,
649
+ configHash
650
+ };
651
+ }
652
+ //#endregion
591
653
  //#region src/namespaces/agent.ts
592
654
  const AGENT_SNAPSHOT_CONFIG_FIELDS = [
593
655
  "name",
@@ -682,6 +744,10 @@ function getProvidedAgentRecordFields(input) {
682
744
  return Object.keys(recordFields).length > 1 ? recordFields : null;
683
745
  }
684
746
  var EditorAgentNamespace = class extends CrudEditorNamespace {
747
+ constructor(..._args) {
748
+ super(..._args);
749
+ this._registeredStoredAgentIds = /* @__PURE__ */ new Set();
750
+ }
685
751
  async getStorageAdapter() {
686
752
  const storage = this.mastra?.getStorage();
687
753
  if (!storage) throw new Error("Storage is not configured");
@@ -817,8 +883,7 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
817
883
  });
818
884
  this.logger?.debug(`[ensureStoredWorkspace] Persisted runtime workspace '${workspaceRef.workspaceId}' to DB`);
819
885
  } else if (workspaceRef.type === "inline") {
820
- const configHash = (0, crypto$1.createHash)("sha256").update(JSON.stringify(workspaceRef.config)).digest("hex").slice(0, 12);
821
- const workspaceId = `inline-${configHash}`;
886
+ const { workspaceId, configHash } = computeInlineWorkspaceIdentity(workspaceRef.config);
822
887
  if (await workspaceNs.getById(workspaceId)) return;
823
888
  await workspaceNs.create({
824
889
  id: workspaceId,
@@ -835,11 +900,22 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
835
900
  }
836
901
  }
837
902
  onCacheEvict(id) {
903
+ this._registeredStoredAgentIds.delete(id);
838
904
  try {
839
905
  if ((this.mastra?.getAgentById(id))?.source === "stored") this.mastra?.removeAgent(id);
840
906
  } catch {}
841
907
  }
842
908
  /**
909
+ * Clear cached agents. Extends the base clear-all to also evict stored agents
910
+ * that were registered with Mastra via version-specific requests, which skip
911
+ * the value cache and would otherwise survive a no-ID clearCache().
912
+ */
913
+ clearCache(id) {
914
+ super.clearCache(id);
915
+ if (id) return;
916
+ for (const registeredId of Array.from(this._registeredStoredAgentIds)) this.onCacheEvict(registeredId);
917
+ }
918
+ /**
843
919
  * Evict all cached agents that reference a given skill ID.
844
920
  * Called by EditorSkillNamespace after a skill is published so that
845
921
  * subsequent agent.getById() calls re-hydrate with the updated skill version.
@@ -1178,7 +1254,10 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1178
1254
  ...skillsFormat && { skillsFormat },
1179
1255
  ...durable !== void 0 && { durable }
1180
1256
  });
1181
- if (!this.getCodeDefinedAgent(storedAgent.id)) this.mastra?.addAgent(agent, storedAgent.id, { source: "stored" });
1257
+ if (!this.getCodeDefinedAgent(storedAgent.id)) {
1258
+ this.mastra?.addAgent(agent, storedAgent.id, { source: "stored" });
1259
+ this._registeredStoredAgentIds.add(storedAgent.id);
1260
+ }
1182
1261
  this.logger?.debug(`[createAgentFromStoredConfig] Successfully created agent "${storedAgent.id}"`);
1183
1262
  return agent;
1184
1263
  }
@@ -1613,8 +1692,8 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1613
1692
  return;
1614
1693
  }
1615
1694
  if (workspaceRef.type === "inline") {
1616
- const configHash = (0, crypto$1.createHash)("sha256").update(JSON.stringify(workspaceRef.config)).digest("hex").slice(0, 12);
1617
- return workspaceNs.hydrateSnapshotToWorkspace(`inline-${configHash}`, workspaceRef.config, hydrateOptions);
1695
+ const { workspaceId } = computeInlineWorkspaceIdentity(workspaceRef.config);
1696
+ return workspaceNs.hydrateSnapshotToWorkspace(workspaceId, workspaceRef.config, hydrateOptions);
1618
1697
  }
1619
1698
  if (workspaceRef.type === "provider") return workspaceNs.resolveWorkspaceProvider(workspaceRef.provider, workspaceRef.config);
1620
1699
  }