@mastra/editor 0.10.2-alpha.1 → 0.11.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,102 @@
1
1
  # @mastra/editor
2
2
 
3
+ ## 0.11.0-alpha.3
4
+
5
+ ### Minor Changes
6
+
7
+ - Added agent override support to the agent and editor APIs. ([#17227](https://github.com/mastra-ai/mastra/pull/17227))
8
+
9
+ Code-defined agents can now declare which fields Studio may edit with the `editor` option:
10
+
11
+ ```ts
12
+ new Agent({
13
+ name: 'Weather Agent',
14
+ model,
15
+ editor: {
16
+ instructions: true,
17
+ tools: { description: true },
18
+ },
19
+ });
20
+ ```
21
+
22
+ The editor applies stored overrides only for fields the `editor` config owns, so locked fields keep their code-defined values. Per-agent `editor: false` locks an agent entirely.
23
+
24
+ `MastraEditor` accepts a `source` setting that picks the editing experience:
25
+
26
+ ```ts
27
+ new MastraEditor({ source: 'code' });
28
+ ```
29
+
30
+ - `source: 'code'` — the editor auto-wires a `FilesystemStore` (defaulting to `./mastra/editor/`, overridable with `codePath`) when no editor storage is supplied, and persists overrides as deterministic per-agent JSON files.
31
+ - `source: 'db'` (default) — keeps the existing storage-backed flow against whatever storage the project has configured.
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [[`a18775a`](https://github.com/mastra-ai/mastra/commit/a18775a693172546ee2378d39b67d4e32895b251), [`1baf2d1`](https://github.com/mastra-ai/mastra/commit/1baf2d152c6881338ff8f114633d5316fe13dd15)]:
36
+ - @mastra/core@1.38.0-alpha.5
37
+
38
+ ## 0.11.0-alpha.2
39
+
40
+ ### Minor Changes
41
+
42
+ - Added the v1 ToolProvider runtime, server routes, client SDK methods, and editor wiring that power OAuth-backed integrations on stored agents. ([#17248](https://github.com/mastra-ai/mastra/pull/17248))
43
+
44
+ **Stored agents can now pin OAuth connections per toolkit**
45
+
46
+ A stored agent's config accepts a new `toolProviders` shape that tells the runtime which connection to bind for each toolkit at execution time. Connections can be scoped per-author, shared across an org, or supplied by the caller.
47
+
48
+ ```ts
49
+ {
50
+ toolProviders: {
51
+ composio: {
52
+ connections: {
53
+ gmail: [{ kind: 'author', toolkit: 'gmail', connectionId: 'auth_abc', scope: 'per-author' }],
54
+ },
55
+ tools: {
56
+ GMAIL_FETCH_EMAILS: { toolkit: 'gmail' },
57
+ },
58
+ },
59
+ },
60
+ }
61
+ ```
62
+
63
+ **New client SDK surface for managing connections**
64
+
65
+ ```ts
66
+ import { MastraClient } from '@mastra/client-js';
67
+
68
+ const client = new MastraClient({ baseUrl: '…' });
69
+ const composio = client.toolProvider('composio');
70
+
71
+ const { items } = await composio.listConnections({ toolkit: 'gmail' });
72
+ await composio.disconnectConnection('auth_abc');
73
+ ```
74
+
75
+ **New `ToolProvider` interface for custom providers**
76
+
77
+ Providers implement a VNext surface (`listToolkitsVNext`, `listToolsVNext`, `resolveToolsVNext`) plus the auth round-trip (`authorize`, `getAuthStatus`, `listConnections`, `disconnectConnection`, `listConnectionFields`, `health`). The Composio provider has been rewritten on this surface; the older catalog methods remain as `@deprecated` shims for back-compat.
78
+
79
+ Connections list responses use `page`/`perPage` pagination, matching the rest of the server surface.
80
+
81
+ Both stored agents (`editor.agent.getById(...)`) and code-defined agents with stored overrides (`editor.agent.applyStoredOverrides(...)`) resolve `toolProviders` at request time, merging provider-resolved tools alongside code/registry/MCP/integration tools.
82
+
83
+ Stored agents that don't set `toolProviders` continue to work unchanged. The Studio/Builder UI ships separately.
84
+
85
+ ### Patch Changes
86
+
87
+ - Improved observability and error isolation in the v1 ToolProvider runtime. ([#17248](https://github.com/mastra-ai/mastra/pull/17248))
88
+
89
+ **Better visibility into connection-scope misconfiguration**
90
+
91
+ When an agent runs with a stored ToolProvider connection whose scope cannot be resolved from the request context, the runtime now logs a one-shot warning and falls back to a shared bucket instead of silently routing every caller to the same OAuth account. Multi-tenant deployments get a clear signal when their identity wiring isn't reaching the runtime.
92
+
93
+ **One bad toolkit no longer disables sibling providers**
94
+
95
+ If a provider returns more connections for a toolkit than its declared capabilities allow, the runtime now logs and skips that toolkit instead of throwing. Other providers and other toolkits on the same agent continue to resolve normally.
96
+
97
+ - Updated dependencies [[`50ed00c`](https://github.com/mastra-ai/mastra/commit/50ed00caa914a85969b33de83f26b48e328ef641), [`9283971`](https://github.com/mastra-ai/mastra/commit/928397157009b4aef4d5fdf3a0a273cb371beb55), [`0bf2d93`](https://github.com/mastra-ai/mastra/commit/0bf2d932d20e2936f2d9abb8c0a86e24fbc97ec6), [`94dfef6`](https://github.com/mastra-ai/mastra/commit/94dfef6e2bf19a88467ea3940afcbce88a433f0f), [`a122f79`](https://github.com/mastra-ai/mastra/commit/a122f79427ae225ec79c7b2ed46278da48d04b17), [`4c02027`](https://github.com/mastra-ai/mastra/commit/4c020277235eaa6b1dc957c90ad0639eef213992), [`6855012`](https://github.com/mastra-ai/mastra/commit/685501247cc4717506f3e89beed03509d63a5370), [`7fef31c`](https://github.com/mastra-ai/mastra/commit/7fef31c0d2a6d362a43a647a8a4f6ab893758a23), [`7fef31c`](https://github.com/mastra-ai/mastra/commit/7fef31c0d2a6d362a43a647a8a4f6ab893758a23)]:
98
+ - @mastra/core@1.38.0-alpha.4
99
+
3
100
  ## 0.10.2-alpha.1
4
101
 
5
102
  ### Patch Changes
package/dist/composio.cjs CHANGED
@@ -25,32 +25,40 @@ __export(composio_exports, {
25
25
  module.exports = __toCommonJS(composio_exports);
26
26
 
27
27
  // src/providers/composio.ts
28
+ var import_tool_provider = require("@mastra/core/tool-provider");
28
29
  var import_request_context = require("@mastra/core/request-context");
29
30
  var import_core = require("@composio/core");
30
31
  var import_mastra = require("@composio/mastra");
31
- var ComposioToolProvider = class {
32
+ var COMPOSIO_PROVIDER_ID = "composio";
33
+ var DEFAULT_INTERNAL_USER_ID = "default";
34
+ var ComposioToolProvider = class extends import_tool_provider.BaseToolProvider {
32
35
  constructor(config) {
36
+ super({
37
+ allowedToolkits: config.allowedToolkits,
38
+ allowedTools: config.allowedTools
39
+ });
33
40
  this.info = {
34
- id: "composio",
41
+ id: COMPOSIO_PROVIDER_ID,
35
42
  name: "Composio",
36
43
  description: "Access 10,000+ tools from 150+ apps via Composio"
37
44
  };
45
+ this.capabilities = {
46
+ multipleConnectionsPerToolkit: true,
47
+ batchConnectionStatus: true,
48
+ reauthorizeReusesConnectionId: true,
49
+ supportsRevoke: true
50
+ };
38
51
  this.rawClient = null;
39
52
  this.mastraClient = null;
40
53
  this.apiKey = config.apiKey;
41
54
  }
42
- /**
43
- * Get or create a raw Composio client (no provider — for discovery only).
44
- */
55
+ // ── client cache ──────────────────────────────────────────────────────
45
56
  getRawClient() {
46
57
  if (!this.rawClient) {
47
58
  this.rawClient = new import_core.Composio({ apiKey: this.apiKey });
48
59
  }
49
60
  return this.rawClient;
50
61
  }
51
- /**
52
- * Get or create a Composio client with MastraProvider (for runtime tools).
53
- */
54
62
  getMastraClient() {
55
63
  if (!this.mastraClient) {
56
64
  this.mastraClient = new import_core.Composio({
@@ -60,85 +68,303 @@ var ComposioToolProvider = class {
60
68
  }
61
69
  return this.mastraClient;
62
70
  }
63
- /**
64
- * List available toolkits via `composio.toolkits.get({})`.
65
- * Returns: `ToolKitListResponse` — an array of `{ slug, name, meta: { description, logo, ... } }`.
66
- */
67
- async listToolkits() {
71
+ // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────
72
+ async listAllToolkits() {
68
73
  const composio = this.getRawClient();
69
74
  const toolkits = await composio.toolkits.get({});
70
- const data = toolkits.map((tk) => ({
75
+ return toolkits.map((tk) => ({
71
76
  slug: tk.slug,
72
77
  name: tk.name,
73
78
  description: tk.meta?.description,
74
79
  icon: tk.meta?.logo
75
80
  }));
76
- return { data };
77
81
  }
78
- /**
79
- * List available tools via `composio.tools.getRawComposioTools()`.
80
- * No userId required — returns raw tool definitions for UI browsing.
81
- */
82
- async listTools(options) {
82
+ async listAllTools(opts) {
83
83
  const composio = this.getRawClient();
84
- const limit = options?.perPage;
85
- const query = options?.toolkit ? { toolkits: [options.toolkit], limit, search: options?.search } : options?.search ? { search: options.search, limit } : { toolkits: [], limit };
86
- const rawTools = await composio.tools.getRawComposioTools(query);
84
+ const limit = opts.perPage;
85
+ const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : void 0;
86
+ const query = opts.toolkit ? { toolkits: [opts.toolkit], limit, search: opts.search } : fallbackToolkits ? { toolkits: fallbackToolkits, limit, search: opts.search } : opts.search ? { search: opts.search, limit } : { toolkits: [], limit };
87
+ let rawTools = [];
88
+ try {
89
+ rawTools = await composio.tools.getRawComposioTools(query);
90
+ } catch (err) {
91
+ console.warn(
92
+ `[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} \u2014 returning empty page`,
93
+ err
94
+ );
95
+ }
87
96
  const data = rawTools.map((tool) => ({
88
97
  slug: tool.slug,
89
98
  name: tool.name ?? tool.slug,
90
99
  description: tool.description,
91
- toolkit: tool.toolkit?.slug
100
+ toolkit: tool.toolkit?.slug ?? opts.toolkit ?? ""
92
101
  }));
93
102
  return {
94
103
  data,
95
104
  pagination: {
96
- page: options?.page ?? 1,
105
+ page: opts.page ?? 1,
97
106
  perPage: limit,
98
107
  hasMore: limit !== void 0 && rawTools.length >= limit
99
108
  }
100
109
  };
101
110
  }
111
+ // ── runtime ───────────────────────────────────────────────────────────
112
+ async resolveToolsVNext(opts) {
113
+ if (opts.toolSlugs.length === 0) return {};
114
+ const internalUserId = opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext);
115
+ const composio = this.getMastraClient();
116
+ const modifiers = {
117
+ // `connectedAccountId` is not threaded through Composio's `execute`
118
+ // option bag in @composio/mastra; the only documented per-call hook
119
+ // is `beforeExecute`, which receives the params object that flows
120
+ // into the API call. Mutating `params.connectedAccountId` routes
121
+ // the call to a specific account.
122
+ beforeExecute: ({ params }) => {
123
+ params.connectedAccountId = opts.connectionId;
124
+ return params;
125
+ }
126
+ };
127
+ const mastraTools = await composio.tools.get(
128
+ internalUserId,
129
+ { tools: opts.toolSlugs },
130
+ modifiers
131
+ );
132
+ const result = {};
133
+ for (const [key, tool] of Object.entries(mastraTools ?? {})) {
134
+ if (!tool) continue;
135
+ const slug = tool.id ?? key;
136
+ try {
137
+ tool.outputSchema = void 0;
138
+ } catch {
139
+ }
140
+ const descOverride = opts.toolMeta?.[slug]?.description;
141
+ if (descOverride) {
142
+ try {
143
+ tool.description = descOverride;
144
+ } catch {
145
+ }
146
+ }
147
+ result[slug] = tool;
148
+ }
149
+ return result;
150
+ }
151
+ // ── auth surface ──────────────────────────────────────────────────────
152
+ async authorize(opts) {
153
+ const composio = this.getRawClient();
154
+ const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);
155
+ const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;
156
+ const initiateConfig = opts.config && Object.keys(opts.config).length > 0 && authScheme ? { authScheme, val: opts.config } : void 0;
157
+ const request = await composio.connectedAccounts.initiate(internalUserId, authConfigId, {
158
+ allowMultiple: true,
159
+ ...initiateConfig ? { config: initiateConfig } : {}
160
+ });
161
+ if (!request.redirectUrl) {
162
+ throw new Error(`[composio] initiate did not return a redirectUrl for toolkit "${opts.toolkit}"`);
163
+ }
164
+ return { url: request.redirectUrl, authId: request.id };
165
+ }
166
+ async listConnectionFields({ toolkit }) {
167
+ const composio = this.getRawClient();
168
+ const { authScheme } = await this.resolveAuthConfig(toolkit);
169
+ if (!authScheme) {
170
+ return [];
171
+ }
172
+ const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {
173
+ requiredOnly: false
174
+ });
175
+ return fields.map((f) => ({
176
+ name: f.name,
177
+ displayName: f.displayName,
178
+ description: f.description,
179
+ type: coerceFieldType(f.type),
180
+ required: f.required ?? false,
181
+ default: f.default ?? void 0
182
+ }));
183
+ }
184
+ async getAuthStatus(authId) {
185
+ const composio = this.getRawClient();
186
+ const account = await composio.connectedAccounts.get(authId);
187
+ switch (account.status) {
188
+ case "ACTIVE":
189
+ return "completed";
190
+ case "INITIALIZING":
191
+ case "INITIATED":
192
+ return "pending";
193
+ case "FAILED":
194
+ case "EXPIRED":
195
+ case "INACTIVE":
196
+ return "failed";
197
+ default:
198
+ return "pending";
199
+ }
200
+ }
201
+ async getConnectionStatus(opts) {
202
+ if (opts.items.length === 0) return {};
203
+ const composio = this.getRawClient();
204
+ const toolkitSlugs = Array.from(new Set(opts.items.map((i) => i.toolkit)));
205
+ const list = await composio.connectedAccounts.list({
206
+ toolkitSlugs
207
+ });
208
+ const liveById = /* @__PURE__ */ new Map();
209
+ for (const item of list.items) {
210
+ liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });
211
+ }
212
+ const result = {};
213
+ for (const { connectionId } of opts.items) {
214
+ const live = liveById.get(connectionId);
215
+ result[connectionId] = { connected: live ? live.status === "ACTIVE" && !live.isDisabled : false };
216
+ }
217
+ return result;
218
+ }
219
+ async listConnections(opts) {
220
+ const composio = this.getRawClient();
221
+ const page = opts.page ?? 1;
222
+ const perPage = clampLimit(opts.perPage);
223
+ const userIds = resolveUserIds(opts);
224
+ if (userIds && userIds.length === 0) {
225
+ return { items: [], pagination: { page, perPage, hasMore: false } };
226
+ }
227
+ const list = await composio.connectedAccounts.list({
228
+ toolkitSlugs: [opts.toolkit],
229
+ ...userIds ? { userIds } : {},
230
+ limit: perPage
231
+ });
232
+ const items = (list.items ?? []).map((account) => ({
233
+ connectionId: account.id,
234
+ status: mapComposioStatus(account.status, account.isDisabled),
235
+ createdAt: account.createdAt,
236
+ // `user_id` is preserved by the Composio SDK transform via spread but
237
+ // isn't on the typed shape. Read it via a narrow cast.
238
+ authorId: account.user_id
239
+ }));
240
+ const nextCursor = list.nextCursor ?? null;
241
+ const hasMore = typeof nextCursor === "string" && nextCursor.length > 0;
242
+ return { items, pagination: { page, perPage, hasMore } };
243
+ }
102
244
  /**
103
- * Get JSON schema for a specific tool via `composio.tools.getRawComposioToolBySlug()`.
245
+ * Revoke a Composio connected account via
246
+ * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft
247
+ * delete and responds with `{ success: boolean }`.
248
+ *
249
+ * Treats a 404 (account already deleted or never existed) as success so
250
+ * the caller can drop its local pin without an error path. A `success:
251
+ * false` response means the provider refused the delete and is surfaced
252
+ * as an error so the caller does not delete its local row.
104
253
  */
105
- async getToolSchema(toolSlug) {
254
+ async revokeConnection(connectionId) {
255
+ const composio = this.getRawClient();
256
+ try {
257
+ const res = await composio.connectedAccounts.delete(connectionId);
258
+ if (res && res.success === false) {
259
+ throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);
260
+ }
261
+ } catch (err) {
262
+ if (isNotFoundError(err)) return;
263
+ throw err;
264
+ }
265
+ }
266
+ async getHealth() {
106
267
  try {
107
268
  const composio = this.getRawClient();
108
- const tool = await composio.tools.getRawComposioToolBySlug(toolSlug);
109
- if (!tool) return null;
110
- return tool.inputParameters ?? {};
111
- } catch {
112
- return null;
269
+ await composio.toolkits.get({ limit: 1 });
270
+ return { ok: true };
271
+ } catch (err) {
272
+ return {
273
+ ok: false,
274
+ message: err instanceof Error ? err.message : "Composio SDK reachability check failed"
275
+ };
113
276
  }
114
277
  }
278
+ // ── helpers ───────────────────────────────────────────────────────────
115
279
  /**
116
- * Resolve executable tools in Mastra format via `composio.tools.get(userId, { tools: [...] })`.
117
- *
118
- * Uses MastraProvider so returned tools are `ReturnType<typeof createTool>` — compatible
119
- * with Mastra's `ToolAction` interface.
280
+ * Resolve the single ENABLED auth config for `toolkit`. Throws if zero
281
+ * or multiple configs match — the admin must enable exactly one in the
282
+ * Composio dashboard before agents can connect.
120
283
  */
121
- async resolveTools(toolSlugs, toolConfigs, options) {
122
- if (toolSlugs.length === 0) return {};
123
- const resourceId = options?.requestContext?.[import_request_context.MASTRA_RESOURCE_ID_KEY];
124
- const userId = typeof resourceId === "string" ? resourceId : options?.userId ?? "default";
125
- const composio = this.getMastraClient();
126
- const mastraTools = await composio.tools.get(userId, { tools: toolSlugs });
127
- const result = {};
128
- const entries = Object.entries(mastraTools ?? {});
129
- for (const [key, tool] of entries) {
130
- if (!tool) continue;
131
- const slug = tool.id ?? key;
132
- const descOverride = toolConfigs?.[slug]?.description;
133
- if (descOverride) {
134
- result[slug] = { ...tool, description: descOverride };
135
- } else {
136
- result[slug] = tool;
137
- }
284
+ async resolveAuthConfig(toolkit) {
285
+ const composio = this.getRawClient();
286
+ const response = await composio.authConfigs.list({ toolkit });
287
+ const enabled = response.items.filter((item) => item.status === "ENABLED");
288
+ if (enabled.length === 0) {
289
+ throw new Error(
290
+ `[composio] No ENABLED auth config for toolkit "${toolkit}". Enable one in the Composio dashboard.`
291
+ );
138
292
  }
139
- return result;
293
+ if (enabled.length > 1) {
294
+ const ids = enabled.map((item) => item.id).join(", ");
295
+ throw new Error(
296
+ `[composio] Multiple ENABLED auth configs for toolkit "${toolkit}" (${ids}). Keep exactly one enabled.`
297
+ );
298
+ }
299
+ return { id: enabled[0].id, authScheme: enabled[0].authScheme };
140
300
  }
141
301
  };
302
+ function isNotFoundError(err) {
303
+ if (!err || typeof err !== "object") return false;
304
+ const e = err;
305
+ if (e.statusCode === 404 || e.status === 404) return true;
306
+ const msg = typeof e.message === "string" ? e.message.toLowerCase() : "";
307
+ return msg.includes("not found") || msg.includes("404");
308
+ }
309
+ function coerceFieldType(type) {
310
+ switch (type.toLowerCase()) {
311
+ case "number":
312
+ case "integer":
313
+ case "int":
314
+ case "float":
315
+ return "number";
316
+ case "bool":
317
+ case "boolean":
318
+ return "boolean";
319
+ default:
320
+ return "string";
321
+ }
322
+ }
323
+ function mapComposioStatus(status, isDisabled) {
324
+ if (isDisabled) return "inactive";
325
+ switch (status) {
326
+ case "ACTIVE":
327
+ return "active";
328
+ case "INITIALIZING":
329
+ case "INITIATED":
330
+ return "pending";
331
+ case "FAILED":
332
+ case "EXPIRED":
333
+ return "failed";
334
+ case "INACTIVE":
335
+ return "inactive";
336
+ default:
337
+ return "pending";
338
+ }
339
+ }
340
+ var MASTRA_USER_KEY = "mastra__user";
341
+ function resolveInternalUserId(requestContext) {
342
+ const resourceId = requestContext?.[import_request_context.MASTRA_RESOURCE_ID_KEY];
343
+ if (typeof resourceId === "string" && resourceId.length > 0) {
344
+ return resourceId;
345
+ }
346
+ const user = requestContext?.[MASTRA_USER_KEY];
347
+ if (user && typeof user === "object" && "id" in user) {
348
+ const id = user.id;
349
+ if (typeof id === "string" && id.length > 0) {
350
+ return id;
351
+ }
352
+ }
353
+ return DEFAULT_INTERNAL_USER_ID;
354
+ }
355
+ function resolveUserIds(opts) {
356
+ if (Array.isArray(opts.userIds)) return opts.userIds;
357
+ if (typeof opts.userId === "string" && opts.userId.length > 0) return [opts.userId];
358
+ return [DEFAULT_INTERNAL_USER_ID];
359
+ }
360
+ var DEFAULT_LIMIT = 50;
361
+ var MAX_LIMIT = 200;
362
+ function clampLimit(limit) {
363
+ if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) {
364
+ return DEFAULT_LIMIT;
365
+ }
366
+ return Math.min(Math.floor(limit), MAX_LIMIT);
367
+ }
142
368
  // Annotate the CommonJS export names for ESM import in node:
143
369
  0 && (module.exports = {
144
370
  ComposioToolProvider
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/composio.ts","../src/providers/composio.ts"],"sourcesContent":["export { ComposioToolProvider } from './providers/composio';\nexport type { ComposioToolProviderConfig } from './providers/composio';\n","import type {\n ToolProvider,\n ToolProviderInfo,\n ToolProviderToolkit,\n ToolProviderToolInfo,\n ToolProviderListResult,\n ListToolProviderToolsOptions,\n ResolveToolProviderToolsOptions,\n} from '@mastra/core/tool-provider';\nimport type { ToolAction } from '@mastra/core/tools';\nimport type { StorageToolConfig } from '@mastra/core/storage';\nimport { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type { Tool as ComposioTool, ToolKitItem, ToolListParams as ComposioToolListParams } from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraTool, MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig {\n /** Composio API key */\n apiKey: string;\n}\n\n/**\n * Composio tool provider adapter.\n *\n * Uses `@composio/core` + `@composio/mastra` SDKs for both tool discovery\n * and runtime resolution. Both packages are optional peer dependencies and\n * tree-shaken if this provider class isn't imported.\n *\n * Discovery methods (`listToolkits`, `listTools`, `getToolSchema`) use the\n * raw Composio client (no userId required).\n *\n * Runtime method (`resolveTools`) uses the MastraProvider so returned tools are\n * already in Mastra's `createTool()` format.\n */\nexport class ComposioToolProvider implements ToolProvider {\n readonly info: ToolProviderInfo = {\n id: 'composio',\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n\n private apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n this.apiKey = config.apiKey;\n }\n\n /**\n * Get or create a raw Composio client (no provider — for discovery only).\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 /**\n * Get or create a Composio client with MastraProvider (for runtime tools).\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 /**\n * List available toolkits via `composio.toolkits.get({})`.\n * Returns: `ToolKitListResponse` — an array of `{ slug, name, meta: { description, logo, ... } }`.\n */\n async listToolkits(): Promise<ToolProviderListResult<ToolProviderToolkit>> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n\n const data: ToolProviderToolkit[] = toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n return { data };\n }\n\n /**\n * List available tools via `composio.tools.getRawComposioTools()`.\n * No userId required — returns raw tool definitions for UI browsing.\n */\n async listTools(options?: ListToolProviderToolsOptions): Promise<ToolProviderListResult<ToolProviderToolInfo>> {\n const composio = this.getRawClient();\n\n // ToolListParams is a discriminated union in TypeScript but the\n // underlying Zod schema accepts `limit` on every variant. We cast\n // through the base type so `limit` is always forwarded.\n const limit = options?.perPage;\n const query: ComposioToolListParams = (\n options?.toolkit\n ? { toolkits: [options.toolkit], limit, search: options?.search }\n : options?.search\n ? { search: options.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n const rawTools: ComposioTool[] = await composio.tools.getRawComposioTools(query);\n\n const data: ToolProviderToolInfo[] = rawTools.map(tool => ({\n slug: tool.slug,\n name: tool.name ?? tool.slug,\n description: tool.description,\n toolkit: tool.toolkit?.slug,\n }));\n\n return {\n data,\n pagination: {\n page: options?.page ?? 1,\n perPage: limit,\n hasMore: limit !== undefined && rawTools.length >= limit,\n },\n };\n }\n\n /**\n * Get JSON schema for a specific tool via `composio.tools.getRawComposioToolBySlug()`.\n */\n async getToolSchema(toolSlug: string): Promise<Record<string, unknown> | null> {\n try {\n const composio = this.getRawClient();\n const tool: ComposioTool = await composio.tools.getRawComposioToolBySlug(toolSlug);\n if (!tool) return null;\n return (tool.inputParameters ?? {}) as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n\n /**\n * Resolve executable tools in Mastra format via `composio.tools.get(userId, { tools: [...] })`.\n *\n * Uses MastraProvider so returned tools are `ReturnType<typeof createTool>` — compatible\n * with Mastra's `ToolAction` interface.\n */\n async resolveTools(\n toolSlugs: string[],\n toolConfigs?: Record<string, StorageToolConfig>,\n options?: ResolveToolProviderToolsOptions,\n ): Promise<Record<string, ToolAction<unknown, unknown>>> {\n if (toolSlugs.length === 0) return {};\n\n const resourceId = options?.requestContext?.[MASTRA_RESOURCE_ID_KEY];\n const userId = typeof resourceId === 'string' ? resourceId : (options?.userId ?? 'default');\n const composio = this.getMastraClient();\n\n // composio.tools.get returns MastraToolCollection = Record<string, MastraTool>\n const mastraTools: MastraToolCollection = await composio.tools.get(userId, { tools: toolSlugs });\n\n const result: Record<string, ToolAction<unknown, unknown>> = {};\n const entries: [string, MastraTool][] = Object.entries(mastraTools ?? {});\n\n for (const [key, tool] of entries) {\n if (!tool) continue;\n const slug = tool.id ?? key;\n const descOverride = toolConfigs?.[slug]?.description;\n // Composio ships tools typed against its bundled Mastra schema version; runtime shape matches ToolAction.\n if (descOverride) {\n result[slug] = { ...tool, description: descOverride } as unknown as ToolAction<unknown, unknown>;\n } else {\n result[slug] = tool as unknown as ToolAction<unknown, unknown>;\n }\n }\n\n return result;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/composio.ts","../src/providers/composio.ts"],"sourcesContent":["export { ComposioToolProvider } from './providers/composio';\nexport type { ComposioToolProviderConfig } from './providers/composio';\n","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} from '@mastra/core/tool-provider';\nimport { BaseToolProvider } from '@mastra/core/tool-provider';\nimport type { BaseToolProviderOptions } from '@mastra/core/tool-provider';\nimport type { ToolAction } from '@mastra/core/tools';\nimport { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';\n\nimport { Composio } from '@composio/core';\nimport type {\n ConnectedAccountListResponse,\n Tool as ComposioTool,\n ToolListParams as ComposioToolListParams,\n ToolKitItem,\n} from '@composio/core';\nimport { MastraProvider } from '@composio/mastra';\nimport type { MastraToolCollection } from '@composio/mastra';\n\nexport interface ComposioToolProviderConfig extends BaseToolProviderOptions {\n /** Composio API key. */\n apiKey: string;\n}\n\nconst COMPOSIO_PROVIDER_ID = 'composio' as const;\nconst DEFAULT_INTERNAL_USER_ID = 'default';\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; each tool gets a\n * `beforeExecute` modifier that injects\n * `connectedAccountId = connectionId`, and `outputSchema` is cleared\n * because Composio returns union schemas that Mastra's runtime rejects.\n *\n * Allowlist filtering is layered by {@link BaseToolProvider}; this class\n * never reads `allowedToolkits` / `allowedTools` directly.\n */\nexport class ComposioToolProvider extends BaseToolProvider {\n readonly info: ToolProviderInfo = {\n id: COMPOSIO_PROVIDER_ID,\n name: 'Composio',\n description: 'Access 10,000+ tools from 150+ apps via Composio',\n };\n readonly capabilities: ToolProviderCapabilities = {\n multipleConnectionsPerToolkit: true,\n batchConnectionStatus: true,\n reauthorizeReusesConnectionId: true,\n supportsRevoke: true,\n };\n\n private readonly apiKey: string;\n private rawClient: Composio | null = null;\n private mastraClient: Composio<MastraProvider> | null = null;\n\n constructor(config: ComposioToolProviderConfig) {\n super({\n allowedToolkits: config.allowedToolkits,\n allowedTools: config.allowedTools,\n });\n this.apiKey = config.apiKey;\n }\n\n // ── client cache ──────────────────────────────────────────────────────\n\n private getRawClient(): Composio {\n if (!this.rawClient) {\n this.rawClient = new Composio({ apiKey: this.apiKey });\n }\n return this.rawClient;\n }\n\n private getMastraClient(): Composio<MastraProvider> {\n if (!this.mastraClient) {\n this.mastraClient = new Composio({\n apiKey: this.apiKey,\n provider: new MastraProvider(),\n });\n }\n return this.mastraClient;\n }\n\n // ── catalog (BaseToolProvider adds allowlist filter on top) ───────────\n\n protected async listAllToolkits(): Promise<ToolProviderToolkit[]> {\n const composio = this.getRawClient();\n const toolkits: ToolKitItem[] = await composio.toolkits.get({});\n return toolkits.map(tk => ({\n slug: tk.slug,\n name: tk.name,\n description: tk.meta?.description,\n icon: tk.meta?.logo,\n }));\n }\n\n protected async listAllTools(opts: ListToolsOpts): Promise<ListToolsResult> {\n const composio = this.getRawClient();\n\n // Composio's `getRawComposioTools` query is a discriminated union — every\n // variant accepts `limit`, but the toolkits/search keys are exclusive in\n // the TS types. We build the variant we need, then cast to the union.\n //\n // When the caller doesn't scope to a specific toolkit, we fall back to\n // the admin allowlist so the SDK returns a flat list across allowed\n // toolkits in a single hop (vs. fanning out per toolkit).\n const limit = opts.perPage;\n const fallbackToolkits = this.allowedToolkits.length > 0 ? [...this.allowedToolkits] : undefined;\n const query: ComposioToolListParams = (\n opts.toolkit\n ? { toolkits: [opts.toolkit], limit, search: opts.search }\n : fallbackToolkits\n ? { toolkits: fallbackToolkits, limit, search: opts.search }\n : opts.search\n ? { search: opts.search, limit }\n : { toolkits: [] as string[], limit }\n ) as ComposioToolListParams;\n\n // Composio's SDK validates every tool's input/output schema against an\n // internal zod shape and throws on the first malformed tool — so one bad\n // toolkit can poison a multi-toolkit query. Treat validation errors as a\n // soft failure and return an empty page rather than a 500.\n let rawTools: ComposioTool[] = [];\n try {\n rawTools = await composio.tools.getRawComposioTools(query);\n } catch (err) {\n console.warn(\n `[ComposioToolProvider] listAllTools failed for query ${JSON.stringify(query)} — returning empty page`,\n err,\n );\n }\n\n const data = rawTools.map(tool => ({\n slug: tool.slug,\n name: tool.name ?? tool.slug,\n description: tool.description,\n toolkit: tool.toolkit?.slug ?? opts.toolkit ?? '',\n }));\n\n return {\n data,\n pagination: {\n page: opts.page ?? 1,\n perPage: limit,\n hasMore: limit !== undefined && rawTools.length >= limit,\n },\n };\n }\n\n // ── runtime ───────────────────────────────────────────────────────────\n\n async resolveToolsVNext(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>> {\n if (opts.toolSlugs.length === 0) return {};\n\n // For author-bound connections, the runtime fan-out passes the agent's\n // author id explicitly. Use it as the Composio user bucket so the pin\n // resolves for any invoker (not just the original author).\n const internalUserId =\n opts.authorId && opts.authorId.length > 0 ? opts.authorId : resolveInternalUserId(opts.requestContext);\n const composio = this.getMastraClient();\n\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 params.connectedAccountId = opts.connectionId;\n return params;\n },\n };\n\n const mastraTools = (await composio.tools.get(\n internalUserId,\n { tools: opts.toolSlugs },\n modifiers,\n )) as MastraToolCollection;\n\n const result: Record<string, ToolAction<any, any, any>> = {};\n\n for (const [key, tool] of Object.entries(mastraTools ?? {})) {\n if (!tool) continue;\n const slug = (tool as { id?: string }).id ?? key;\n\n // Composio returns union output schemas (`successful: true | false`) that\n // Mastra's runtime cannot validate; clearing avoids per-tool validation\n // errors at execute time. The property may be non-writable on some SDK\n // versions, so we swallow assignment errors.\n try {\n (tool as unknown as { outputSchema: unknown }).outputSchema = undefined;\n } catch {\n // ignore\n }\n\n const descOverride = opts.toolMeta?.[slug]?.description;\n if (descOverride) {\n try {\n (tool as unknown as { description: string }).description = descOverride;\n } catch {\n // ignore\n }\n }\n\n result[slug] = tool as ToolAction<any, any, any>;\n }\n\n return result;\n }\n\n // ── auth surface ──────────────────────────────────────────────────────\n\n async authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }> {\n const composio = this.getRawClient();\n const { id: authConfigId, authScheme } = await this.resolveAuthConfig(opts.toolkit);\n\n // `connectionId` carries the internal user bucket for the runtime fan-out;\n // for authorize we treat it as the Composio `userId` so the new connected\n // account lands under the same bucket as the agent's resolved identity.\n const internalUserId = opts.connectionId || DEFAULT_INTERNAL_USER_ID;\n // `allowMultiple: true` — we explicitly support N connected accounts per\n // (user, auth config) and disambiguate at runtime via per-connection labels.\n // `config` carries provider-specific user-supplied fields (e.g. Confluence\n // subdomain) collected by the picker via `listConnectionFields`. Composio\n // expects a discriminated `{ authScheme, val }` shape; we cast through\n // `unknown` because our generic interface keeps it Record-shaped.\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 const request = await composio.connectedAccounts.initiate(internalUserId, authConfigId, {\n allowMultiple: true,\n ...(initiateConfig ? { config: initiateConfig } : {}),\n });\n\n if (!request.redirectUrl) {\n throw new Error(`[composio] initiate did not return a redirectUrl for toolkit \"${opts.toolkit}\"`);\n }\n\n return { url: request.redirectUrl, authId: request.id };\n }\n\n async listConnectionFields({ toolkit }: { toolkit: string }): Promise<ConnectionField[]> {\n const composio = this.getRawClient();\n const { authScheme } = await this.resolveAuthConfig(toolkit);\n if (!authScheme) {\n // Without a known auth scheme we can't query the field schema — fall\n // back to no fields rather than blocking the user.\n return [];\n }\n const fields = await composio.toolkits.getConnectedAccountInitiationFields(toolkit, authScheme, {\n requiredOnly: false,\n });\n return fields.map(f => ({\n name: f.name,\n displayName: f.displayName,\n description: f.description,\n type: coerceFieldType(f.type),\n required: f.required ?? false,\n default: f.default ?? undefined,\n }));\n }\n\n async getAuthStatus(authId: string): Promise<AuthFlowStatus> {\n const composio = this.getRawClient();\n const account = await composio.connectedAccounts.get(authId);\n switch (account.status) {\n case 'ACTIVE':\n return 'completed';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n case 'INACTIVE':\n return 'failed';\n default:\n return 'pending';\n }\n }\n\n async getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>> {\n if (opts.items.length === 0) return {};\n\n const composio = this.getRawClient();\n const toolkitSlugs = Array.from(new Set(opts.items.map(i => i.toolkit)));\n\n // One SDK call per `getConnectionStatus`, regardless of N items.\n // Filter by all referenced toolkits, then bucket locally by id.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs,\n });\n\n const liveById = new Map<string, { status: string; isDisabled: boolean }>();\n for (const item of list.items) {\n liveById.set(item.id, { status: item.status, isDisabled: item.isDisabled });\n }\n\n const result: Record<string, { connected: boolean }> = {};\n for (const { connectionId } of opts.items) {\n const live = liveById.get(connectionId);\n result[connectionId] = { connected: live ? live.status === 'ACTIVE' && !live.isDisabled : false };\n }\n return result;\n }\n\n async listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult> {\n const composio = this.getRawClient();\n const page = opts.page ?? 1;\n const perPage = clampLimit(opts.perPage);\n\n // Normalize userIds[] / userId. Empty array = no buckets to list against,\n // short-circuit to avoid an unbounded Composio response.\n const userIds = resolveUserIds(opts);\n if (userIds && userIds.length === 0) {\n return { items: [], pagination: { page, perPage, hasMore: false } };\n }\n\n // Composio SDK 0.6.x uses cursor-based pagination on the wire. We surface\n // page-based pagination to keep the Mastra contract consistent with every\n // other list API. For now we only fetch the first page (page=1); paginated\n // requests for page > 1 are a follow-up — the UI does not yet paginate.\n const list: ConnectedAccountListResponse = await composio.connectedAccounts.list({\n toolkitSlugs: [opts.toolkit],\n ...(userIds ? { userIds } : {}),\n limit: perPage,\n });\n\n // Defensive: tolerate undocumented SDK shape drift where `items` is\n // missing or `nextCursor` is `null`/`undefined`/`''`.\n const items: ExistingConnection[] = (list.items ?? []).map(account => ({\n connectionId: account.id,\n status: mapComposioStatus(account.status, account.isDisabled),\n createdAt: account.createdAt,\n // `user_id` is preserved by the Composio SDK transform via spread but\n // isn't on the typed shape. Read it via a narrow cast.\n authorId: (account as unknown as { user_id?: string }).user_id,\n }));\n\n const nextCursor = (list as { nextCursor?: string | null }).nextCursor ?? null;\n const hasMore = typeof nextCursor === 'string' && nextCursor.length > 0;\n return { items, pagination: { page, perPage, hasMore } };\n }\n\n /**\n * Revoke a Composio connected account via\n * `DELETE /api/v3/connected_accounts/:nanoid`. Composio performs a soft\n * delete and responds with `{ success: boolean }`.\n *\n * Treats a 404 (account already deleted or never existed) as success so\n * the caller can drop its local pin without an error path. A `success:\n * false` response means the provider refused the delete and is surfaced\n * as an error so the caller does not delete its local row.\n */\n async revokeConnection(connectionId: string): Promise<void> {\n const composio = this.getRawClient();\n try {\n const res = (await composio.connectedAccounts.delete(connectionId)) as { success?: boolean } | undefined;\n if (res && res.success === false) {\n throw new Error(`Composio refused to delete connected account ${connectionId} (success=false)`);\n }\n } catch (err) {\n if (isNotFoundError(err)) return;\n throw err;\n }\n }\n\n async getHealth(): Promise<ToolProviderHealth> {\n try {\n const composio = this.getRawClient();\n await composio.toolkits.get({ limit: 1 } as Parameters<typeof composio.toolkits.get>[0]);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n message: err instanceof Error ? err.message : 'Composio SDK reachability check failed',\n };\n }\n }\n\n // ── helpers ───────────────────────────────────────────────────────────\n\n /**\n * Resolve the single ENABLED auth config for `toolkit`. Throws if zero\n * or multiple configs match — the admin must enable exactly one in the\n * Composio dashboard before agents can connect.\n */\n private async resolveAuthConfig(toolkit: string): Promise<{ id: string; authScheme?: ComposioAuthScheme }> {\n const composio = this.getRawClient();\n const response = await composio.authConfigs.list({ toolkit });\n const enabled = response.items.filter(item => item.status === 'ENABLED');\n\n if (enabled.length === 0) {\n throw new Error(\n `[composio] No ENABLED auth config for toolkit \"${toolkit}\". Enable one in the Composio dashboard.`,\n );\n }\n if (enabled.length > 1) {\n const ids = enabled.map(item => item.id).join(', ');\n throw new Error(\n `[composio] Multiple ENABLED auth configs for toolkit \"${toolkit}\" (${ids}). Keep exactly one enabled.`,\n );\n }\n return { id: enabled[0]!.id, authScheme: enabled[0]!.authScheme };\n }\n}\n\ntype ComposioAuthScheme = NonNullable<\n Awaited<ReturnType<Composio['authConfigs']['list']>>['items'][number]['authScheme']\n>;\n\n/**\n * Best-effort 404 detection across the various error shapes the Composio\n * SDK surfaces (typed error with `statusCode`, HTTP-like error with\n * `status`, or a plain message containing \"404\" / \"not found\").\n */\nfunction isNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as { statusCode?: number; status?: number; message?: string };\n if (e.statusCode === 404 || e.status === 404) return true;\n const msg = typeof e.message === 'string' ? e.message.toLowerCase() : '';\n return msg.includes('not found') || msg.includes('404');\n}\n\n/**\n * Composio reports a free-form `type` string. Map common values to our\n * generic ConnectionField type vocabulary; everything else falls back to\n * `'string'`.\n */\nfunction coerceFieldType(type: string): 'string' | 'number' | 'boolean' {\n switch (type.toLowerCase()) {\n case 'number':\n case 'integer':\n case 'int':\n case 'float':\n return 'number';\n case 'bool':\n case 'boolean':\n return 'boolean';\n default:\n return 'string';\n }\n}\n\n/**\n * Map Composio account status + `isDisabled` to the {@link ExistingConnection}\n * status vocabulary surfaced to the picker UI.\n */\nfunction mapComposioStatus(status: string, isDisabled: boolean): ExistingConnection['status'] {\n if (isDisabled) return 'inactive';\n switch (status) {\n case 'ACTIVE':\n return 'active';\n case 'INITIALIZING':\n case 'INITIATED':\n return 'pending';\n case 'FAILED':\n case 'EXPIRED':\n return 'failed';\n case 'INACTIVE':\n return 'inactive';\n default:\n return 'pending';\n }\n}\n\n// Mirror of `MASTRA_USER_KEY` from `@mastra/server`. Inlined to avoid a\n// reverse dependency from `editor` onto `server`.\nconst MASTRA_USER_KEY = 'mastra__user';\n\n/**\n * Read the internal user id (Composio `userId`) from per-request context.\n *\n * The runtime fan-out is responsible for stamping the agent's resolved\n * author id (or `'default'`) into `requestContext` under\n * {@link MASTRA_RESOURCE_ID_KEY}.\n */\nfunction resolveInternalUserId(requestContext?: Record<string, unknown>): string {\n const resourceId = requestContext?.[MASTRA_RESOURCE_ID_KEY];\n if (typeof resourceId === 'string' && resourceId.length > 0) {\n return resourceId;\n }\n\n const user = requestContext?.[MASTRA_USER_KEY];\n if (user && typeof user === 'object' && 'id' in user) {\n const id = (user as { id: unknown }).id;\n if (typeof id === 'string' && id.length > 0) {\n return id;\n }\n }\n\n return DEFAULT_INTERNAL_USER_ID;\n}\n\n/**\n * Resolve `userIds[]` from `listConnections` opts.\n *\n * - If `userIds` is provided, use it as-is (including empty array, which\n * means \"no buckets to list against\").\n * - If `userId` is provided, normalize to `[userId]`.\n * - Otherwise fall back to the default internal user id (single-bucket).\n */\nfunction resolveUserIds(opts: ListConnectionsOpts): string[] | undefined {\n if (Array.isArray(opts.userIds)) return opts.userIds;\n if (typeof opts.userId === 'string' && opts.userId.length > 0) return [opts.userId];\n return [DEFAULT_INTERNAL_USER_ID];\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 200;\n\nfunction clampLimit(limit: number | undefined): number {\n if (typeof limit !== 'number' || !Number.isFinite(limit) || limit <= 0) {\n return DEFAULT_LIMIT;\n }\n return Math.min(Math.floor(limit), MAX_LIMIT);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeA,2BAAiC;AAGjC,6BAAuC;AAEvC,kBAAyB;AAOzB,oBAA+B;AAQ/B,IAAM,uBAAuB;AAC7B,IAAM,2BAA2B;AAe1B,IAAM,uBAAN,cAAmC,sCAAiB;AAAA,EAiBzD,YAAY,QAAoC;AAC9C,UAAM;AAAA,MACJ,iBAAiB,OAAO;AAAA,MACxB,cAAc,OAAO;AAAA,IACvB,CAAC;AApBH,SAAS,OAAyB;AAAA,MAChC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AACA,SAAS,eAAyC;AAAA,MAChD,+BAA+B;AAAA,MAC/B,uBAAuB;AAAA,MACvB,+BAA+B;AAAA,MAC/B,gBAAgB;AAAA,IAClB;AAGA,SAAQ,YAA6B;AACrC,SAAQ,eAAgD;AAOtD,SAAK,SAAS,OAAO;AAAA,EACvB;AAAA;AAAA,EAIQ,eAAyB;AAC/B,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,IAAI,qBAAS,EAAE,QAAQ,KAAK,OAAO,CAAC;AAAA,IACvD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,kBAA4C;AAClD,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,eAAe,IAAI,qBAAS;AAAA,QAC/B,QAAQ,KAAK;AAAA,QACb,UAAU,IAAI,6BAAe;AAAA,MAC/B,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAIA,MAAgB,kBAAkD;AAChE,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,WAA0B,MAAM,SAAS,SAAS,IAAI,CAAC,CAAC;AAC9D,WAAO,SAAS,IAAI,SAAO;AAAA,MACzB,MAAM,GAAG;AAAA,MACT,MAAM,GAAG;AAAA,MACT,aAAa,GAAG,MAAM;AAAA,MACtB,MAAM,GAAG,MAAM;AAAA,IACjB,EAAE;AAAA,EACJ;AAAA,EAEA,MAAgB,aAAa,MAA+C;AAC1E,UAAM,WAAW,KAAK,aAAa;AASnC,UAAM,QAAQ,KAAK;AACnB,UAAM,mBAAmB,KAAK,gBAAgB,SAAS,IAAI,CAAC,GAAG,KAAK,eAAe,IAAI;AACvF,UAAM,QACJ,KAAK,UACD,EAAE,UAAU,CAAC,KAAK,OAAO,GAAG,OAAO,QAAQ,KAAK,OAAO,IACvD,mBACE,EAAE,UAAU,kBAAkB,OAAO,QAAQ,KAAK,OAAO,IACzD,KAAK,SACH,EAAE,QAAQ,KAAK,QAAQ,MAAM,IAC7B,EAAE,UAAU,CAAC,GAAe,MAAM;AAO5C,QAAI,WAA2B,CAAC;AAChC,QAAI;AACF,iBAAW,MAAM,SAAS,MAAM,oBAAoB,KAAK;AAAA,IAC3D,SAAS,KAAK;AACZ,cAAQ;AAAA,QACN,wDAAwD,KAAK,UAAU,KAAK,CAAC;AAAA,QAC7E;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,SAAS,IAAI,WAAS;AAAA,MACjC,MAAM,KAAK;AAAA,MACX,MAAM,KAAK,QAAQ,KAAK;AAAA,MACxB,aAAa,KAAK;AAAA,MAClB,SAAS,KAAK,SAAS,QAAQ,KAAK,WAAW;AAAA,IACjD,EAAE;AAEF,WAAO;AAAA,MACL;AAAA,MACA,YAAY;AAAA,QACV,MAAM,KAAK,QAAQ;AAAA,QACnB,SAAS;AAAA,QACT,SAAS,UAAU,UAAa,SAAS,UAAU;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,kBAAkB,MAA4E;AAClG,QAAI,KAAK,UAAU,WAAW,EAAG,QAAO,CAAC;AAKzC,UAAM,iBACJ,KAAK,YAAY,KAAK,SAAS,SAAS,IAAI,KAAK,WAAW,sBAAsB,KAAK,cAAc;AACvG,UAAM,WAAW,KAAK,gBAAgB;AAEtC,UAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMhB,eAAe,CAAC,EAAE,OAAO,MAAoE;AAC3F,eAAO,qBAAqB,KAAK;AACjC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,cAAe,MAAM,SAAS,MAAM;AAAA,MACxC;AAAA,MACA,EAAE,OAAO,KAAK,UAAU;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,SAAoD,CAAC;AAE3D,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,eAAe,CAAC,CAAC,GAAG;AAC3D,UAAI,CAAC,KAAM;AACX,YAAM,OAAQ,KAAyB,MAAM;AAM7C,UAAI;AACF,QAAC,KAA8C,eAAe;AAAA,MAChE,QAAQ;AAAA,MAER;AAEA,YAAM,eAAe,KAAK,WAAW,IAAI,GAAG;AAC5C,UAAI,cAAc;AAChB,YAAI;AACF,UAAC,KAA4C,cAAc;AAAA,QAC7D,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,aAAO,IAAI,IAAI;AAAA,IACjB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,MAAM,UAAU,MAA+D;AAC7E,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,EAAE,IAAI,cAAc,WAAW,IAAI,MAAM,KAAK,kBAAkB,KAAK,OAAO;AAKlF,UAAM,iBAAiB,KAAK,gBAAgB;AAO5C,UAAM,iBACJ,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,KAAK,aACjD,EAAE,YAAY,KAAK,KAAK,OAAO,IAOhC;AACN,UAAM,UAAU,MAAM,SAAS,kBAAkB,SAAS,gBAAgB,cAAc;AAAA,MACtF,eAAe;AAAA,MACf,GAAI,iBAAiB,EAAE,QAAQ,eAAe,IAAI,CAAC;AAAA,IACrD,CAAC;AAED,QAAI,CAAC,QAAQ,aAAa;AACxB,YAAM,IAAI,MAAM,iEAAiE,KAAK,OAAO,GAAG;AAAA,IAClG;AAEA,WAAO,EAAE,KAAK,QAAQ,aAAa,QAAQ,QAAQ,GAAG;AAAA,EACxD;AAAA,EAEA,MAAM,qBAAqB,EAAE,QAAQ,GAAoD;AACvF,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,EAAE,WAAW,IAAI,MAAM,KAAK,kBAAkB,OAAO;AAC3D,QAAI,CAAC,YAAY;AAGf,aAAO,CAAC;AAAA,IACV;AACA,UAAM,SAAS,MAAM,SAAS,SAAS,oCAAoC,SAAS,YAAY;AAAA,MAC9F,cAAc;AAAA,IAChB,CAAC;AACD,WAAO,OAAO,IAAI,QAAM;AAAA,MACtB,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,aAAa,EAAE;AAAA,MACf,MAAM,gBAAgB,EAAE,IAAI;AAAA,MAC5B,UAAU,EAAE,YAAY;AAAA,MACxB,SAAS,EAAE,WAAW;AAAA,IACxB,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,cAAc,QAAyC;AAC3D,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,UAAU,MAAM,SAAS,kBAAkB,IAAI,MAAM;AAC3D,YAAQ,QAAQ,QAAQ;AAAA,MACtB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAM,oBAAoB,MAE0B;AAClD,QAAI,KAAK,MAAM,WAAW,EAAG,QAAO,CAAC;AAErC,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,eAAe,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,IAAI,OAAK,EAAE,OAAO,CAAC,CAAC;AAIvE,UAAM,OAAqC,MAAM,SAAS,kBAAkB,KAAK;AAAA,MAC/E;AAAA,IACF,CAAC;AAED,UAAM,WAAW,oBAAI,IAAqD;AAC1E,eAAW,QAAQ,KAAK,OAAO;AAC7B,eAAS,IAAI,KAAK,IAAI,EAAE,QAAQ,KAAK,QAAQ,YAAY,KAAK,WAAW,CAAC;AAAA,IAC5E;AAEA,UAAM,SAAiD,CAAC;AACxD,eAAW,EAAE,aAAa,KAAK,KAAK,OAAO;AACzC,YAAM,OAAO,SAAS,IAAI,YAAY;AACtC,aAAO,YAAY,IAAI,EAAE,WAAW,OAAO,KAAK,WAAW,YAAY,CAAC,KAAK,aAAa,MAAM;AAAA,IAClG;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,gBAAgB,MAA2D;AAC/E,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,UAAU,WAAW,KAAK,OAAO;AAIvC,UAAM,UAAU,eAAe,IAAI;AACnC,QAAI,WAAW,QAAQ,WAAW,GAAG;AACnC,aAAO,EAAE,OAAO,CAAC,GAAG,YAAY,EAAE,MAAM,SAAS,SAAS,MAAM,EAAE;AAAA,IACpE;AAMA,UAAM,OAAqC,MAAM,SAAS,kBAAkB,KAAK;AAAA,MAC/E,cAAc,CAAC,KAAK,OAAO;AAAA,MAC3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC7B,OAAO;AAAA,IACT,CAAC;AAID,UAAM,SAA+B,KAAK,SAAS,CAAC,GAAG,IAAI,cAAY;AAAA,MACrE,cAAc,QAAQ;AAAA,MACtB,QAAQ,kBAAkB,QAAQ,QAAQ,QAAQ,UAAU;AAAA,MAC5D,WAAW,QAAQ;AAAA;AAAA;AAAA,MAGnB,UAAW,QAA4C;AAAA,IACzD,EAAE;AAEF,UAAM,aAAc,KAAwC,cAAc;AAC1E,UAAM,UAAU,OAAO,eAAe,YAAY,WAAW,SAAS;AACtE,WAAO,EAAE,OAAO,YAAY,EAAE,MAAM,SAAS,QAAQ,EAAE;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,iBAAiB,cAAqC;AAC1D,UAAM,WAAW,KAAK,aAAa;AACnC,QAAI;AACF,YAAM,MAAO,MAAM,SAAS,kBAAkB,OAAO,YAAY;AACjE,UAAI,OAAO,IAAI,YAAY,OAAO;AAChC,cAAM,IAAI,MAAM,gDAAgD,YAAY,kBAAkB;AAAA,MAChG;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,gBAAgB,GAAG,EAAG;AAC1B,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,YAAyC;AAC7C,QAAI;AACF,YAAM,WAAW,KAAK,aAAa;AACnC,YAAM,SAAS,SAAS,IAAI,EAAE,OAAO,EAAE,CAAgD;AACvF,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,kBAAkB,SAA2E;AACzG,UAAM,WAAW,KAAK,aAAa;AACnC,UAAM,WAAW,MAAM,SAAS,YAAY,KAAK,EAAE,QAAQ,CAAC;AAC5D,UAAM,UAAU,SAAS,MAAM,OAAO,UAAQ,KAAK,WAAW,SAAS;AAEvE,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,IAAI;AAAA,QACR,kDAAkD,OAAO;AAAA,MAC3D;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,MAAM,QAAQ,IAAI,UAAQ,KAAK,EAAE,EAAE,KAAK,IAAI;AAClD,YAAM,IAAI;AAAA,QACR,yDAAyD,OAAO,MAAM,GAAG;AAAA,MAC3E;AAAA,IACF;AACA,WAAO,EAAE,IAAI,QAAQ,CAAC,EAAG,IAAI,YAAY,QAAQ,CAAC,EAAG,WAAW;AAAA,EAClE;AACF;AAWA,SAAS,gBAAgB,KAAuB;AAC9C,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,MAAI,EAAE,eAAe,OAAO,EAAE,WAAW,IAAK,QAAO;AACrD,QAAM,MAAM,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,YAAY,IAAI;AACtE,SAAO,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,KAAK;AACxD;AAOA,SAAS,gBAAgB,MAA+C;AACtE,UAAQ,KAAK,YAAY,GAAG;AAAA,IAC1B,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAMA,SAAS,kBAAkB,QAAgB,YAAmD;AAC5F,MAAI,WAAY,QAAO;AACvB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAIA,IAAM,kBAAkB;AASxB,SAAS,sBAAsB,gBAAkD;AAC/E,QAAM,aAAa,iBAAiB,6CAAsB;AAC1D,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,GAAG;AAC3D,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,iBAAiB,eAAe;AAC7C,MAAI,QAAQ,OAAO,SAAS,YAAY,QAAQ,MAAM;AACpD,UAAM,KAAM,KAAyB;AACrC,QAAI,OAAO,OAAO,YAAY,GAAG,SAAS,GAAG;AAC3C,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,eAAe,MAAiD;AACvE,MAAI,MAAM,QAAQ,KAAK,OAAO,EAAG,QAAO,KAAK;AAC7C,MAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,SAAS,EAAG,QAAO,CAAC,KAAK,MAAM;AAClF,SAAO,CAAC,wBAAwB;AAClC;AAEA,IAAM,gBAAgB;AACtB,IAAM,YAAY;AAElB,SAAS,WAAW,OAAmC;AACrD,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;AACtE,WAAO;AAAA,EACT;AACA,SAAO,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,SAAS;AAC9C;","names":[]}