diff --git a/apps/rowboat/app/lib/copilot/copilot.ts b/apps/rowboat/app/lib/copilot/copilot.ts index 2112d9cb..d0276ebf 100644 --- a/apps/rowboat/app/lib/copilot/copilot.ts +++ b/apps/rowboat/app/lib/copilot/copilot.ts @@ -59,6 +59,16 @@ const ZDoneEvent = z.object({ const ZEvent = z.union([ZTextEvent, ZToolCallEvent, ZToolResultEvent, ZDoneEvent]); +const composioToolSearchToolSuggestion = z.object({ + toolkit: z.string(), + tool_slug: z.string(), + description: z.string(), +}); +const composioToolSearchResponseSchema = z.object({ + results: z.array(composioToolSearchToolSuggestion), + related_tools: z.array(composioToolSearchToolSuggestion), +}); + function getContextPrompt(context: z.infer | null): string { let prompt = ''; switch (context?.type) { @@ -127,16 +137,23 @@ async function searchRelevantTools(query: string): Promise { arguments: { use_case: query }, }); - if (!searchResult.successful || !Array.isArray(searchResult.data?.results)) { - logger.log("tool search was not successful or returned no results"); - console.log("❌ TOOL CALL FAILED: COMPOSIO_SEARCH_TOOLS", { - successful: searchResult.successful, - results: searchResult.data?.results - }); - return ''; + if (!searchResult.successful) { + logger.log(`tool search failed: ${searchResult.error}`) + return 'No tools found!'; } - const toolSlugs: string[] = searchResult.data.results.map((result: any) => result.tool); + // parse results + const result = composioToolSearchResponseSchema.safeParse(searchResult.data); + if (!result.success) { + logger.log(`tool search response is invalid: ${result.error}`); + return 'No tools found!'; + } + if (!result.data.results.length) { + logger.log(`tool search yielded no results`); + return 'No tools found!'; + } + + const toolSlugs = result.data.results.map((item) => item.tool_slug); logger.log(`found tool slugs: ${toolSlugs.join(', ')}`); console.log("✅ TOOL CALL SUCCESS: COMPOSIO_SEARCH_TOOLS", { toolSlugs, diff --git a/apps/rowboat/app/lib/types/workflow_types.ts b/apps/rowboat/app/lib/types/workflow_types.ts index edd03a04..4432b426 100644 --- a/apps/rowboat/app/lib/types/workflow_types.ts +++ b/apps/rowboat/app/lib/types/workflow_types.ts @@ -26,7 +26,8 @@ export const WorkflowAgent = z.object({ 'relinquish_to_start', ]).optional().describe('Whether this agent retains control after a turn, relinquishes to the parent agent, or relinquishes to the start agent'), maxCallsPerParentAgent: z.number().default(3).describe('Maximum number of times this agent can be called by a parent agent in a single turn').optional(), -}).refine((data) => { +}); +export const StrictWorkflowAgent = WorkflowAgent.refine((data) => { // Pipeline agents should have internal output visibility and relinquish_to_parent control type if (data.type === 'pipeline' && data.outputVisibility !== 'internal') { return false;