diff --git a/apps/x/apps/main/src/composio-handler.ts b/apps/x/apps/main/src/composio-handler.ts index 3289c9c3..59532373 100644 --- a/apps/x/apps/main/src/composio-handler.ts +++ b/apps/x/apps/main/src/composio-handler.ts @@ -4,7 +4,7 @@ import * as composioClient from '@x/core/dist/composio/client.js'; import { composioAccountsRepo } from '@x/core/dist/composio/repo.js'; import { invalidateCopilotInstructionsCache } from '@x/core/dist/application/assistant/instructions.js'; import { CURATED_TOOLKIT_SLUGS } from '@x/shared/dist/composio.js'; -import type { LocalConnectedAccount } from '@x/core/dist/composio/types.js'; +import type { LocalConnectedAccount, Toolkit } from '@x/core/dist/composio/types.js'; import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gmail.js'; import { triggerSync as triggerCalendarSync } from '@x/core/dist/knowledge/sync_calendar.js'; @@ -312,8 +312,7 @@ export async function useComposioForGoogleCalendar(): Promise<{ enabled: boolean */ export async function listToolkits() { // Paginate through all API pages to collect every curated toolkit - type ToolkitItem = Awaited>['items'][number]; - const allItems: ToolkitItem[] = []; + const allItems: Toolkit[] = []; let cursor: string | null = null; const maxPages = 10; // safety limit for (let page = 0; page < maxPages; page++) { diff --git a/apps/x/packages/core/src/composio/client.ts b/apps/x/packages/core/src/composio/client.ts index 722cb4a4..2844fc28 100644 --- a/apps/x/packages/core/src/composio/client.ts +++ b/apps/x/packages/core/src/composio/client.ts @@ -15,7 +15,6 @@ import { ZExecuteActionResponse, ZListResponse, ZSearchResultTool, - ZTool, ZToolkit, type NormalizedToolResult, } from "./types.js"; @@ -284,21 +283,6 @@ export async function deleteConnectedAccount(connectedAccountId: string): Promis }); } -/** - * Infer toolkit slug from a tool slug when the API omits the toolkit object. - * Convention: TOOLKIT_ACTION (e.g., GITHUB_CREATE_ISSUE → github). - * - * This fallback exists because the Composio /tools search endpoint occasionally - * returns results without the `toolkit` field — observed with certain search - * queries and when the tool is from a less-common integration. The inference is - * a best-effort heuristic: lowercase the first segment before the first underscore. - */ -function inferToolkitSlug(toolSlug: string): string { - const lower = toolSlug.toLowerCase(); - const parts = lower.split('_'); - return parts[0] ?? lower; -} - /** * Search for tools across all toolkits (or optionally filtered by specific toolkit slugs). * Returns tools with full input_parameters so the agent knows what params to pass. @@ -311,7 +295,7 @@ export async function searchTools( toolkitSlugs?: string[], ): Promise<{ items: NormalizedToolResult[] }> { const params: Record = { - search: searchQuery, + query: searchQuery, limit: '50', }; if (toolkitSlugs && toolkitSlugs.length === 1) { @@ -324,9 +308,7 @@ export async function searchTools( slug: item.slug, name: item.name, description: item.description, - toolkitSlug: item.toolkit?.slug - || inferToolkitSlug(item.slug) - || (toolkitSlugs?.length === 1 ? toolkitSlugs[0] : ''), + toolkitSlug: item.toolkit.slug, inputParameters: { type: 'object' as const, properties: item.input_parameters?.properties ?? {}, diff --git a/apps/x/packages/core/src/composio/types.ts b/apps/x/packages/core/src/composio/types.ts index 5585d63f..b9ff4915 100644 --- a/apps/x/packages/core/src/composio/types.ts +++ b/apps/x/packages/core/src/composio/types.ts @@ -1,4 +1,8 @@ import { z } from "zod"; +import { ZToolkitMeta as ZSharedToolkitMeta, ZToolkitItem } from "@x/shared/dist/composio.js"; + +// Re-export the shared toolkit schemas so existing imports continue to work +export const ZToolkitMeta = ZSharedToolkitMeta; /** * Composio authentication schemes @@ -29,28 +33,9 @@ export const ZConnectedAccountStatus = z.enum([ ]); /** - * Toolkit metadata + * Toolkit schema — same shape as ZToolkitItem from shared, re-exported for convenience. */ -export const ZToolkitMeta = z.object({ - description: z.string(), - logo: z.string(), - tools_count: z.number(), - triggers_count: z.number(), -}); - -/** - * Toolkit schema - */ -export const ZToolkit = z.object({ - slug: z.string(), - name: z.string(), - meta: ZToolkitMeta, - no_auth: z.boolean().optional(), - // Use z.string() instead of ZAuthScheme to be resilient against - // new auth types added by the Composio API over time. - auth_schemes: z.array(z.string()).optional(), - composio_managed_auth_schemes: z.array(z.string()).optional(), -}); +export const ZToolkit = ZToolkitItem; /** * Tool schema @@ -246,7 +231,7 @@ export const ZSearchResultTool = z.object({ slug: z.string(), name: z.string(), logo: z.string(), - }).optional(), + }), input_parameters: z.object({ type: z.literal('object').optional().default('object'), properties: z.record(z.string(), z.unknown()).optional().default({}), diff --git a/apps/x/packages/shared/src/composio.ts b/apps/x/packages/shared/src/composio.ts index c904c9a3..8a15e8d2 100644 --- a/apps/x/packages/shared/src/composio.ts +++ b/apps/x/packages/shared/src/composio.ts @@ -1,3 +1,31 @@ +import { z } from 'zod'; + +/** + * Zod schemas for Composio IPC responses. + * Defined here in shared so both ipc.ts and core/composio/types.ts can reference them. + */ +export const ZToolkitMeta = z.object({ + description: z.string(), + logo: z.string(), + tools_count: z.number(), + triggers_count: z.number(), +}); + +export const ZToolkitItem = z.object({ + slug: z.string(), + name: z.string(), + meta: ZToolkitMeta, + no_auth: z.boolean().optional(), + auth_schemes: z.array(z.string()).optional(), + composio_managed_auth_schemes: z.array(z.string()).optional(), +}); + +export const ZListToolkitsResponse = z.object({ + items: z.array(ZToolkitItem), + nextCursor: z.string().nullable(), + totalItems: z.number(), +}); + /** * Curated Composio toolkits available to Rowboat users. * Single source of truth for slugs, display names, and categories. diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts index fae87f7f..7e32a4fc 100644 --- a/apps/x/packages/shared/src/ipc.ts +++ b/apps/x/packages/shared/src/ipc.ts @@ -8,6 +8,7 @@ import { AgentScheduleState } from './agent-schedule-state.js'; import { ServiceEvent } from './service-events.js'; import { UserMessageContent } from './message.js'; import { RowboatApiConfig } from './rowboat-account.js'; +import { ZListToolkitsResponse } from './composio.js'; // ============================================================================ // Runtime Validation Schemas (Single Source of Truth) @@ -398,26 +399,9 @@ const ipcSchemas = { res: z.null(), }, // Composio Tools Library channels - // Response schema mirrors core/composio/types.ts ZToolkit (kept inline to avoid cross-package import) 'composio:list-toolkits': { req: z.object({}), - res: z.object({ - items: z.array(z.object({ - slug: z.string(), - name: z.string(), - meta: z.object({ - description: z.string(), - logo: z.string(), - tools_count: z.number(), - triggers_count: z.number(), - }), - no_auth: z.boolean().optional(), - auth_schemes: z.array(z.string()).optional(), - composio_managed_auth_schemes: z.array(z.string()).optional(), - })), - nextCursor: z.string().nullable(), - totalItems: z.number(), - }), + res: ZListToolkitsResponse, }, // Agent schedule channels 'agent-schedule:getConfig': {