Skip to content

Conversation

@sidneyswift
Copy link
Collaborator

@sidneyswift sidneyswift commented Jan 8, 2026

  • Add generic Composio toolkit infrastructure (toolkits.ts, authenticateToolkit.ts, getConnectedAccount.ts)
  • Add Google Drive login flow with OAuth redirect
  • Refactor Google Sheets to use shared Composio utilities (DRY)
  • Add composioAgent for toolkit-agnostic tool fetching
  • Add reusable ComposioLoginLoading and ComposioLoginResult components
  • Use window.location.origin for dev/prod redirect compatibility

Summary by CodeRabbit

  • New Features

    • Added Google Drive integration with login authentication.
  • Improvements

    • Standardized login flows and UI components for Google integrations.
    • Enhanced consistency across toolkit authentication experiences.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add generic Composio toolkit infrastructure (toolkits.ts, authenticateToolkit.ts, getConnectedAccount.ts)
- Add Google Drive login flow with OAuth redirect
- Refactor Google Sheets to use shared Composio utilities (DRY)
- Add composioAgent for toolkit-agnostic tool fetching
- Add reusable ComposioLoginLoading and ComposioLoginResult components
- Use window.location.origin for dev/prod redirect compatibility
@vercel
Copy link

vercel bot commented Jan 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
recoup-chat Ready Ready Preview Jan 8, 2026 7:41pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

This PR refactors Composio toolkit authentication by introducing a generalized toolkit framework. It consolidates Google Sheets and Google Drive login flows through shared UI components, a generic login hook architecture, and centralized toolkit configuration, replacing toolkit-specific implementations with reusable abstractions.

Changes

Cohort / File(s) Summary
Google Drive Login UI
components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx, components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
New components that integrate Google Drive authentication into the shared Composio login UI framework.
Shared Composio Login Components
components/VercelChat/tools/composio/ComposioLoginLoading.tsx, components/VercelChat/tools/composio/ComposioLoginResult.tsx
New reusable UI components for toolkit login flows, replacing inline loading/result logic.
Google Sheets Login UI (Refactored)
components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx, components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
Migrated from inline UI to shared ComposioLoginLoading and ComposioLoginResult components; removed local state management.
ToolComponents Dispatcher
components/VercelChat/ToolComponents.tsx
Extended tool call/result rendering to handle googleDriveLoginTool via new GoogleDriveLoginLoading and GoogleDriveLoginResult components.
Login Hooks
hooks/useComposioLogin.ts, hooks/useGoogleDriveLogin.ts, hooks/useGoogleSheetsLogin.ts
New generic useComposioLogin hook that centralizes toolkit login logic; toolkit-specific hooks now delegate to it. Removed inline state and API calls from useGoogleSheetsLogin.
Toolkit Configuration & Helpers
lib/composio/toolkits.ts, lib/composio/authenticateToolkit.ts, lib/composio/getConnectedAccount.ts, lib/composio/fetchConnectedAccountsRefresh.ts
New modules providing centralized toolkit metadata (COMPOSIO_TOOLKITS), authentication initiation, connected account retrieval, and refresh logic with common interfaces.
Generic Toolkit Tools
lib/agents/composioAgent/getToolkitTools.ts, lib/agents/composioAgent/index.ts
New generic function to retrieve toolkit tools, replacing toolkit-specific implementations; determines whether to return active tools or a login tool.
Login Tool Factory
lib/tools/composio/loginTools.ts
New factory that creates reusable login tools for any toolkit; pre-instantiates tools for GOOGLE_SHEETS and GOOGLE_DRIVE; exports backward-compatible aliases.
Tool Setup Integration
lib/chat/setupToolsForRequest.ts
Updated to use parallel getToolkitTools calls for multiple toolkits (GOOGLE_SHEETS and GOOGLE_DRIVE) instead of toolkit-specific functions.
Removed Google Sheets-Specific Modules
lib/agents/googleSheetsAgent/getGoogleSheetsTools.ts, lib/agents/googleSheetsAgent/index.ts, lib/composio/googleSheets/authenticateGoogleSheetsToolkit.ts, lib/composio/googleSheets/fetchConnectedAccountsRefresh.ts, lib/composio/googleSheets/getConnectedAccount.ts, lib/tools/composio/googleSheetsLoginTool.ts
Eliminated toolkit-specific implementations now covered by generalized Composio toolkit framework.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UIComponent as Google Drive Login UI
    participant Hook as useGoogleDriveLogin
    participant API as fetchConnectedAccountsRefresh
    participant RecoupAPI as Recoup API
    participant ComposioClient as Composio Client

    User->>UIComponent: Click "Connect Google Drive"
    UIComponent->>Hook: Call handleLogin()
    Hook->>API: Fetch redirect URL with accountId
    API->>RecoupAPI: POST refresh request
    RecoupAPI-->>API: Return redirect URL
    API-->>Hook: Return response
    Hook->>ComposioClient: Navigate to redirect_url
    ComposioClient->>User: Redirect to Google auth
    Note over User,ComposioClient: User completes OAuth flow
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sweetmantech

🏗️ From scattered sheets and drives so wide,
One toolkit framework, unified with pride.
Login flows consolidated, clean and bright,
Generic tools that work just right!

🚥 Pre-merge checks | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning Pull request demonstrates inconsistent application of SOLID principles across codebase with hardcoded URLs, missing null checks, improper className handling, and flawed async retry logic. Apply consistent environment variable patterns, add null checks for accountId, use cn() utility for classNames, replace Promise.all with Promise.allSettled, and refactor OAuth retry logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 408ff1e438

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +33 to +37
// Get tools for all Composio toolkits
const [googleSheetsTools, googleDriveTools] = await Promise.all([
getToolkitTools("GOOGLE_SHEETS", body),
getToolkitTools("GOOGLE_DRIVE", body),
]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard Drive toolkit when env var is missing

The new unconditional getToolkitTools("GOOGLE_DRIVE", …) call means every chat request now tries to resolve Drive tools. When a user has no existing Drive connection, getToolkitTools → getConnectedAccount → authenticateToolkit reads COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID and throws if it isn’t set. In any environment where Drive isn’t configured (or for new users), this will fail the entire tool setup even if Drive isn’t requested. Consider skipping Drive when the env var is absent or deferring the call until the user requests Drive.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @components/VercelChat/tools/composio/ComposioLoginResult.tsx:
- Line 6: The ComposioLoginResult component hardcodes "Google" and uses
string-concatenated classNames; make the redirect service dynamic (add a prop
like serviceName or redirectService or read it from useUserProvider()) and
replace the literal "Google" text with that prop/value throughout the component
so non-Google integrations work; also replace the className usage
className={`w-full ${buttonColor} text-white`} with the cn() utility (e.g.,
cn('w-full', buttonColor, 'text-white')) to merge classes properly while keeping
the existing import of useUserProvider (whose filename currently has the typo
UserProvder) unchanged.

In @hooks/useComposioLogin.ts:
- Line 21: The hook useComposioLogin currently force-casts userData?.account_id
to a string via accountId = userData?.account_id as string; — change this to
null-safe handling: validate that userData and userData.account_id exist (e.g.,
derive accountId as string | null or undefined instead of forcing a cast),
return that accountId from the hook, and update any API call paths inside the
hook (and consuming components) to early-return or disable operations when
accountId is missing; ensure the returned shape includes accountId so callers
can disable buttons when no account is available.

In @lib/agents/composioAgent/getToolkitTools.ts:
- Around line 30-34: The callbackUrl in the CreateConnectedAccountOptions object
is using a hardcoded production domain; update the options object so callbackUrl
is built from an environment-specific origin instead: if this code runs
client-side use window.location.origin, otherwise read a configured environment
variable (e.g., process.env.APP_ORIGIN) and fall back to a sensible default,
then append the encoded latestUserMessageText as before (refer to options,
CreateConnectedAccountOptions, callbackUrl, latestUserMessageText).

In @lib/chat/setupToolsForRequest.ts:
- Around line 34-37: Replace the Promise.all call that fetches toolkit tools so
a single toolkit failure does not bail the whole request: call
Promise.allSettled with the two getToolkitTools("GOOGLE_SHEETS", body) and
getToolkitTools("GOOGLE_DRIVE", body) promises, inspect each result, set
googleSheetsTools and googleDriveTools to the fulfilled values (or an empty
array/null) when rejected, and log the rejection errors via your logging
utility; ensure subsequent code that uses googleSheetsTools/googleDriveTools can
handle missing/empty values so the request proceeds with available toolkits.

In @lib/composio/getConnectedAccount.ts:
- Around line 28-34: getConnectedAccount currently calls authenticateToolkit and
then immediately re-invokes composio.connectedAccounts.list, which assumes OAuth
completes synchronously; instead, stop retrying immediately after
authenticateToolkit. Modify getConnectedAccount so that after calling
authenticateToolkit it returns a clear pending/auth-initiated result (or throws
a specific AuthPending error) that the caller can handle (e.g., trigger browser
flow and poll later), or alternatively document that the caller must re-run/get
accounts after the OAuth flow completes; do not call
composio.connectedAccounts.list again directly after authenticateToolkit. Ensure
references to authenticateToolkit, composio.connectedAccounts.list, and the
userAccounts handling are updated accordingly.
🧹 Nitpick comments (13)
components/VercelChat/tools/composio/ComposioLoginResult.tsx (2)

60-63: Hardcoded "Google" breaks component reusability.

This shared component hardcodes "Google" in the authorization notice, which won't be accurate for non-Google toolkits added in the future. Consider parameterizing the provider name or deriving it from toolkitName.

♻️ Suggested fix
-      <p className="text-xs text-muted-foreground text-center">
-        You&apos;ll be redirected to Google to authorize access to your{" "}
-        {toolkitName} for this account.
-      </p>
+      <p className="text-xs text-muted-foreground text-center">
+        You&apos;ll be redirected to authorize access to your {toolkitName} for
+        this account.
+      </p>

44-58: Consider using CVA for button color variants.

Per coding guidelines, use class-variance-authority (CVA) for component variants. Passing buttonColor as a raw class string works but bypasses the variant pattern used elsewhere in the codebase. This is a minor consistency nit for future extensibility.

hooks/useComposioLogin.ts (2)

27-27: Consider safer optional chaining for window access.

While the typeof check works, using optional chaining provides more defensive coding against edge cases.

♻️ Optional refinement
-   const baseUrl = typeof window !== "undefined" ? window.location.origin : "https://chat.recoupable.com";
+   const baseUrl = window?.location?.origin ?? "https://chat.recoupable.com";

This pattern is more concise and handles nullish values more gracefully.


45-48: Add type guard for error handling.

The error in the catch block is typed as unknown (implicitly), but config.name is used without verifying that config is valid. While getToolkitConfig likely always returns a valid config, defensive error handling is recommended.

🛡️ Improved error handling
    } catch (error) {
+     console.error(`Failed to initiate ${config.name} login:`, error);
      toast.error(
        `Failed to initiate ${config.name} login. Please try again.`
      );
    } finally {

Adding a console.error helps with debugging in production while maintaining the user-friendly toast message. This follows the guideline that console.log should be removed, but console.error for actual errors is appropriate.

lib/composio/authenticateToolkit.ts (1)

13-28: Add error handling with context for authentication failures.

The function lacks explicit error handling around authentication operations. When getToolkitAuthConfigId or composio.connectedAccounts.initiate fails, the raw error propagates to callers without additional context. Consider wrapping the operations in a try-catch block to provide more descriptive error messages that include the toolkit key and user ID.

♻️ Proposed error handling enhancement
 export async function authenticateToolkit(
   toolkitKey: ComposioToolkitKey,
   userId: string,
   options?: CreateConnectedAccountOptions
 ) {
-  const authConfigId = getToolkitAuthConfigId(toolkitKey);
-  const composio = await getComposioClient();
-
-  const connectionRequest = await composio.connectedAccounts.initiate(
-    userId,
-    authConfigId,
-    options
-  );
-
-  return connectionRequest;
+  try {
+    const authConfigId = getToolkitAuthConfigId(toolkitKey);
+    const composio = await getComposioClient();
+
+    const connectionRequest = await composio.connectedAccounts.initiate(
+      userId,
+      authConfigId,
+      options
+    );
+
+    return connectionRequest;
+  } catch (error) {
+    throw new Error(
+      `Failed to authenticate ${toolkitKey} for user ${userId}: ${error instanceof Error ? error.message : 'Unknown error'}`
+    );
+  }
 }
lib/agents/composioAgent/getToolkitTools.ts (1)

21-48: Consider separating authentication and tool retrieval concerns.

The function handles both checking authentication state and retrieving tools, which violates the Single Responsibility Principle. While the current implementation is functional, consider whether the authentication check logic could be extracted to improve testability and reusability.

components/VercelChat/tools/composio/ComposioLoginLoading.tsx (2)

10-15: Add accessibility attributes for screen reader support.

The loading component lacks ARIA attributes to communicate its purpose and dynamic state to assistive technologies. Add role="status" and aria-live="polite" to ensure screen readers announce the loading state.

♿ Proposed accessibility enhancement
   return (
-    <div className="flex items-center gap-1 py-1 px-2 bg-muted/50 rounded-sm border border-border w-fit text-xs text-muted-foreground">
+    <div 
+      role="status" 
+      aria-live="polite"
+      aria-label={`Initializing ${toolkitName} login`}
+      className="flex items-center gap-1 py-1 px-2 bg-muted/50 rounded-sm border border-border w-fit text-xs text-muted-foreground"
+    >
       <Loader className="h-3 w-3 animate-spin text-foreground" />
       <span>Initializing {toolkitName} login...</span>
     </div>
   );

18-18: Remove redundant default export.

The component has both a named export (line 7) and a default export (line 18). The named export is sufficient and more explicit. Per coding guidelines: "Use Single Responsibility Principle: One function per file with clear naming."

♻️ Proposed cleanup
-
-export default ComposioLoginLoading;
lib/composio/fetchConnectedAccountsRefresh.ts (2)

43-45: Enhance error messages with response details.

The error message only includes the HTTP status code but lacks context about which toolkit failed and the response body (which may contain useful error details from the API). Include the toolkit key and attempt to parse the error response.

🔧 Proposed error handling enhancement
   if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`);
+    const errorBody = await response.text().catch(() => 'Unable to parse error response');
+    throw new Error(
+      `Failed to refresh ${toolkitKey} connected account (HTTP ${response.status}): ${errorBody}`
+    );
   }

32-41: Add timeout handling for fetch requests.

The fetch request lacks timeout handling, which could cause the request to hang indefinitely if the API is unresponsive. Consider adding an AbortController with a timeout to improve reliability.

⏱️ Proposed timeout handling
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
+
+  try {
     const response = await fetch(`${API_BASE_URL}${config.refreshEndpoint}`, {
       method: "POST",
       headers: {
         "Content-Type": "application/json",
       },
       body: JSON.stringify({
         accountId,
         redirectUrl,
       }),
+      signal: controller.signal,
     });
+    clearTimeout(timeoutId);
+  } catch (error) {
+    clearTimeout(timeoutId);
+    if (error instanceof Error && error.name === 'AbortError') {
+      throw new Error(`Request to refresh ${toolkitKey} timed out`);
+    }
+    throw error;
+  }
lib/composio/getConnectedAccount.ts (2)

42-46: Improve safety of connection status check.

The isConnectionActive function uses optional chaining on items[0] but doesn't explicitly check if the array has items. While technically safe due to optional chaining, it returns false for both "no accounts" and "account exists but inactive", which may be ambiguous for callers.

🛡️ Proposed safer implementation
 export function isConnectionActive(
   userAccounts: Awaited<ReturnType<typeof getConnectedAccount>>
 ): boolean {
-  return userAccounts.items[0]?.data?.status === "ACTIVE";
+  return (
+    userAccounts.items.length > 0 &&
+    userAccounts.items[0]?.data?.status === "ACTIVE"
+  );
 }

Alternatively, consider returning a more descriptive type:

export function getConnectionStatus(
  userAccounts: Awaited<ReturnType<typeof getConnectedAccount>>
): "active" | "inactive" | "none" {
  if (userAccounts.items.length === 0) return "none";
  return userAccounts.items[0]?.data?.status === "ACTIVE" ? "active" : "inactive";
}

15-37: Consider separating account retrieval from authentication initiation.

The getConnectedAccount function handles both retrieving accounts and initiating authentication when none exist. This violates the Single Responsibility Principle and makes the function's behavior less predictable. Consider either:

  1. Renaming to getOrInitiateConnectedAccount to clarify dual behavior
  2. Splitting into separate functions: getConnectedAccount and initiateConnection

The current approach may be acceptable given the OAuth flow, but explicit naming would improve code clarity.

lib/tools/composio/loginTools.ts (1)

24-30: Consider adding error handling in the execute function.

The execute function calls getConnectedAccount without a try/catch. If the connected account lookup or authentication flow fails, the error will propagate unhandled, which could result in a poor user experience or unclear error messages from the AI.

Wrapping this in error handling would provide more graceful degradation and clearer feedback.

♻️ Suggested improvement
     execute: async ({ account_id }) => {
-      await getConnectedAccount(toolkitKey, account_id);
-      return {
-        success: true,
-        message: `${config.name} login initiated successfully. Please click the button above to login with ${config.name}.`,
-      };
+      try {
+        await getConnectedAccount(toolkitKey, account_id);
+        return {
+          success: true,
+          message: `${config.name} login initiated successfully. Please click the button above to login with ${config.name}.`,
+        };
+      } catch (error) {
+        return {
+          success: false,
+          message: `Failed to initiate ${config.name} login. Please try again.`,
+        };
+      }
     },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69bfe82 and 408ff1e.

📒 Files selected for processing (24)
  • components/VercelChat/ToolComponents.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • hooks/useComposioLogin.ts
  • hooks/useGoogleDriveLogin.ts
  • hooks/useGoogleSheetsLogin.ts
  • lib/agents/composioAgent/getToolkitTools.ts
  • lib/agents/composioAgent/index.ts
  • lib/agents/googleSheetsAgent/getGoogleSheetsTools.ts
  • lib/agents/googleSheetsAgent/index.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/composio/authenticateToolkit.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • lib/composio/getConnectedAccount.ts
  • lib/composio/googleSheets/authenticateGoogleSheetsToolkit.ts
  • lib/composio/googleSheets/fetchConnectedAccountsRefresh.ts
  • lib/composio/googleSheets/getConnectedAccount.ts
  • lib/composio/toolkits.ts
  • lib/tools/composio/googleSheetsLoginTool.ts
  • lib/tools/composio/loginTools.ts
💤 Files with no reviewable changes (6)
  • lib/agents/googleSheetsAgent/index.ts
  • lib/composio/googleSheets/authenticateGoogleSheetsToolkit.ts
  • lib/composio/googleSheets/fetchConnectedAccountsRefresh.ts
  • lib/composio/googleSheets/getConnectedAccount.ts
  • lib/tools/composio/googleSheetsLoginTool.ts
  • lib/agents/googleSheetsAgent/getGoogleSheetsTools.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/component-design.mdc)

**/*.{tsx,ts,jsx,js}: Use semantic HTML elements appropriate to the component's role
Ensure keyboard navigation and focus management in UI components
Provide proper ARIA roles/states and test with screen readers
Start with semantic HTML first, then augment with ARIA if needed
Support both controlled and uncontrolled state in components
Wrap a single HTML element per component (no multiple root elements)
Favor composition over inheritance in component design
Expose clear APIs via props/slots for component customization
Use asChild pattern for flexible element types in components
Support polymorphism with as prop when appropriate
Use @radix-ui/react-* primitives for behavior and accessibility
Use class-variance-authority (CVA) for component variants
Use @radix-ui/react-slot for implementing asChild pattern
Use @radix-ui/react-use-controllable-state for state management
Follow shadcn/ui component structure and naming conventions
Use cn() utility combining clsx and tailwind-merge for class merging
Export both component and variant functions (e.g., Button, buttonVariants)
Use @radix-ui/react-icons for UI component icons (not Lucide React)
Use lucide-react for application icons and illustrations
Use framer-motion for animations and transitions
Use next-themes for theme management
Use tailwindcss-animate for CSS animations
Use sonner for toast notifications
Use embla-carousel-react for carousels
Use react-resizable-panels for resizable layouts
Provide defaultValue and onValueChange props for state management
Use data-state attribute for visual component states (open/closed, loading, etc.)
Use data-slot attribute for component identification
Use kebab-case naming for data attributes (e.g., data-slot="form-field")
Follow class order in Tailwind: base → variants → conditionals → user overrides
Define component variants outside components to avoid recreation on re-render
Implement focus trapping in modal/dialog components
Store...

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • hooks/useGoogleDriveLogin.ts
  • hooks/useComposioLogin.ts
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • lib/tools/composio/loginTools.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • lib/agents/composioAgent/getToolkitTools.ts
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • lib/composio/authenticateToolkit.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/agents/composioAgent/index.ts
  • components/VercelChat/ToolComponents.tsx
  • lib/composio/toolkits.ts
  • hooks/useGoogleSheetsLogin.ts
  • lib/composio/getConnectedAccount.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/component-design.mdc)

**/*.{tsx,ts}: Extend native HTML attributes using React.ComponentProps<"element"> pattern
Export component prop types with explicit ComponentNameProps naming convention

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • hooks/useGoogleDriveLogin.ts
  • hooks/useComposioLogin.ts
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • lib/tools/composio/loginTools.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • lib/agents/composioAgent/getToolkitTools.ts
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • lib/composio/authenticateToolkit.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/agents/composioAgent/index.ts
  • components/VercelChat/ToolComponents.tsx
  • lib/composio/toolkits.ts
  • hooks/useGoogleSheetsLogin.ts
  • lib/composio/getConnectedAccount.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/stagehand.mdc)

**/*.{ts,tsx}: Plan instructions using Stagehand observe method before executing actions (e.g., const results = await page.observe("Click the sign in button");)
Cache the results of observe to avoid unexpected DOM changes
Keep act actions atomic and specific (e.g., "Click the sign in button" not "Sign in to the website"). Avoid actions that are more than one step.
Use variable substitution with the variables parameter in act for dynamic form filling (e.g., action: "Enter Name: %name%", variables: { name: "John Doe" })
Always use Zod schemas for structured data extraction with the extract method (e.g., schema: z.object({ text: z.string() }))
Wrap array extractions in a single object when extracting multiple items using extract (e.g., schema: z.object({ buttons: z.array(z.string()) }))
Use explicit instruction and schema parameters when calling extract method
Import Stagehand types from @browserbasehq/stagehand package (e.g., import { Stagehand, Page, BrowserContext } from "@browserbasehq/stagehand";)
Initialize Stagehand with new Stagehand() constructor and call await stagehand.init() before using page automation methods
Implement main automation logic in functions that accept { page, context, stagehand } parameters
Provide specific instructions to agents instead of vague ones (e.g., "Navigate to products page and filter by 'Electronics'" not "Do some stuff on this page")
Break down complex agent tasks into smaller steps for better execution
Use error handling with try/catch blocks when executing agent tasks
Combine agents for navigation with traditional methods for precise data extraction

**/*.{ts,tsx}: Create single-responsibility components with obvious data flow
Use strict TypeScript types with zero 'any' types
Follow Next.js optimization guides for performance

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • hooks/useGoogleDriveLogin.ts
  • hooks/useComposioLogin.ts
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • lib/tools/composio/loginTools.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • lib/agents/composioAgent/getToolkitTools.ts
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • lib/composio/authenticateToolkit.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/agents/composioAgent/index.ts
  • components/VercelChat/ToolComponents.tsx
  • lib/composio/toolkits.ts
  • hooks/useGoogleSheetsLogin.ts
  • lib/composio/getConnectedAccount.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,js}: Write minimal code - use only the absolute minimum code needed
Ensure code is self-documenting through precise naming with verbs for functions and nouns for variables
Add short comments only when necessary to clarify non-obvious logic
Implement built-in security practices for authentication and data handling
Consider if code can be split into smaller functions before implementing
Avoid unnecessary abstractions during implementation
Ensure code clarity is suitable for understanding by junior developers

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • hooks/useGoogleDriveLogin.ts
  • hooks/useComposioLogin.ts
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • lib/tools/composio/loginTools.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • lib/agents/composioAgent/getToolkitTools.ts
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • lib/composio/authenticateToolkit.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/agents/composioAgent/index.ts
  • components/VercelChat/ToolComponents.tsx
  • lib/composio/toolkits.ts
  • hooks/useGoogleSheetsLogin.ts
  • lib/composio/getConnectedAccount.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use Single Responsibility Principle: One function per file with clear naming
Remove all console.log statements before merging to production
Write comments to explain 'why', not 'what'; prefer self-documenting code

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • hooks/useGoogleDriveLogin.ts
  • hooks/useComposioLogin.ts
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • lib/tools/composio/loginTools.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • lib/agents/composioAgent/getToolkitTools.ts
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • lib/composio/authenticateToolkit.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/agents/composioAgent/index.ts
  • components/VercelChat/ToolComponents.tsx
  • lib/composio/toolkits.ts
  • hooks/useGoogleSheetsLogin.ts
  • lib/composio/getConnectedAccount.ts
**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/components/**/*.{ts,tsx}: Use @radix-ui/react-* primitives with class-variance-authority (CVA) for component styling
Use cn() utility for class merging in components
Always include accessibility in components: use semantic HTML, ARIA attributes, and keyboard navigation

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • components/VercelChat/ToolComponents.tsx
components/**/*.tsx

⚙️ CodeRabbit configuration file

components/**/*.tsx: For React components, ensure:

  • Single responsibility per component
  • Proper prop interfaces (ISP)
  • Use composition over inheritance
  • Avoid prop drilling (use context or state management)
  • Keep components under 200 lines
  • Extract custom hooks for complex logic
  • Use TypeScript interfaces for props
  • DRY: Extract common UI patterns into reusable components
  • KISS: Prefer simple component structure over complex abstractions

Files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx
  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
  • components/VercelChat/ToolComponents.tsx
hooks/**/*.ts

⚙️ CodeRabbit configuration file

hooks/**/*.ts: For custom hooks, ensure:

  • Single responsibility per hook
  • Return consistent interface
  • Handle edge cases and errors
  • Use proper dependency arrays
  • Keep hooks focused and reusable
  • Follow naming convention (use prefix)
  • DRY: Extract common hook logic into shared utilities
  • KISS: Avoid complex hook compositions, prefer simple state management

Files:

  • hooks/useGoogleDriveLogin.ts
  • hooks/useComposioLogin.ts
  • hooks/useGoogleSheetsLogin.ts
lib/**/*.ts

⚙️ CodeRabbit configuration file

lib/**/*.ts: For utility functions, ensure:

  • Pure functions when possible
  • Single responsibility per function
  • Proper error handling
  • Use TypeScript for type safety
  • Avoid side effects
  • Keep functions under 50 lines
  • DRY: Consolidate similar logic into shared utilities
  • KISS: Prefer simple, readable implementations over clever optimizations

Files:

  • lib/tools/composio/loginTools.ts
  • lib/composio/fetchConnectedAccountsRefresh.ts
  • lib/agents/composioAgent/getToolkitTools.ts
  • lib/composio/authenticateToolkit.ts
  • lib/chat/setupToolsForRequest.ts
  • lib/agents/composioAgent/index.ts
  • lib/composio/toolkits.ts
  • lib/composio/getConnectedAccount.ts
🧠 Learnings (3)
📚 Learning: 2025-11-29T16:42:25.945Z
Learnt from: CR
Repo: Recoupable-com/Recoup-Chat PR: 0
File: .cursor/rules/component-design.mdc:0-0
Timestamp: 2025-11-29T16:42:25.945Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use `lucide-react` for application icons and illustrations

Applied to files:

  • components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginLoading.tsx
  • components/VercelChat/tools/composio/ComposioLoginResult.tsx
📚 Learning: 2026-01-07T17:34:21.911Z
Learnt from: CR
Repo: Recoupable-com/Recoup-Chat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-07T17:34:21.911Z
Learning: Applies to **/lib/chat/toolChains/**/*.{ts,tsx} : MCP tools like `send_email` are provided by the recoup-api MCP server; reference tool names in tool chains at `lib/chat/toolChains/` instead of defining tools locally

Applied to files:

  • lib/chat/setupToolsForRequest.ts
  • components/VercelChat/ToolComponents.tsx
📚 Learning: 2026-01-07T17:34:21.911Z
Learnt from: CR
Repo: Recoupable-com/Recoup-Chat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-07T17:34:21.911Z
Learning: Applies to **/lib/consts.ts : Store shared constants in `lib/consts.ts` including `RECOUP_FROM_EMAIL` for email sender address and platform-specific configurations

Applied to files:

  • lib/composio/toolkits.ts
🧬 Code graph analysis (13)
components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx (2)
hooks/useGoogleSheetsLogin.ts (1)
  • useGoogleSheetsLogin (6-8)
components/VercelChat/tools/composio/ComposioLoginResult.tsx (1)
  • ComposioLoginResult (18-66)
components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx (1)
components/VercelChat/tools/composio/ComposioLoginLoading.tsx (1)
  • ComposioLoginLoading (7-16)
hooks/useGoogleDriveLogin.ts (1)
hooks/useComposioLogin.ts (1)
  • useComposioLogin (17-59)
components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx (1)
components/VercelChat/tools/composio/ComposioLoginLoading.tsx (1)
  • ComposioLoginLoading (7-16)
lib/tools/composio/loginTools.ts (2)
lib/composio/toolkits.ts (2)
  • ComposioToolkitKey (26-26)
  • getToolkitConfig (32-34)
lib/composio/getConnectedAccount.ts (1)
  • getConnectedAccount (15-37)
lib/composio/fetchConnectedAccountsRefresh.ts (1)
lib/composio/toolkits.ts (2)
  • ComposioToolkitKey (26-26)
  • getToolkitConfig (32-34)
components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx (2)
hooks/useGoogleDriveLogin.ts (1)
  • useGoogleDriveLogin (6-8)
components/VercelChat/tools/composio/ComposioLoginResult.tsx (1)
  • ComposioLoginResult (18-66)
components/VercelChat/tools/composio/ComposioLoginResult.tsx (1)
components/ui/button.tsx (1)
  • Button (57-57)
lib/composio/authenticateToolkit.ts (2)
lib/composio/toolkits.ts (2)
  • ComposioToolkitKey (26-26)
  • getToolkitAuthConfigId (39-50)
lib/composio/client.ts (1)
  • getComposioClient (11-16)
lib/chat/setupToolsForRequest.ts (2)
lib/agents/composioAgent/getToolkitTools.ts (1)
  • getToolkitTools (21-48)
lib/chat/filterExcludedTools.ts (1)
  • filterExcludedTools (9-24)
components/VercelChat/ToolComponents.tsx (2)
components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx (1)
  • GoogleDriveLoginLoading (3-5)
components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx (1)
  • GoogleDriveLoginResult (7-20)
hooks/useGoogleSheetsLogin.ts (1)
hooks/useComposioLogin.ts (1)
  • useComposioLogin (17-59)
lib/composio/getConnectedAccount.ts (3)
lib/composio/toolkits.ts (2)
  • ComposioToolkitKey (26-26)
  • getToolkitConfig (32-34)
lib/composio/client.ts (1)
  • getComposioClient (11-16)
lib/composio/authenticateToolkit.ts (1)
  • authenticateToolkit (13-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Run evaluations
🔇 Additional comments (16)
lib/composio/toolkits.ts (1)

1-50: Well-structured toolkit configuration with proper error handling.

This establishes a clean single source of truth for Composio toolkits. The use of as const ensures type safety, and getToolkitAuthConfigId appropriately throws with a descriptive message when the environment variable is missing. Good adherence to the Single Responsibility Principle.

lib/agents/composioAgent/index.ts (1)

1-1: Clean barrel export.

Simple and follows the standard module entry point pattern.

hooks/useGoogleDriveLogin.ts (1)

1-8: Clean hook composition following established patterns.

This follows the same pattern as useGoogleSheetsLogin, promoting consistency and DRY principles. The delegation to useComposioLogin keeps toolkit-specific hooks thin and maintainable.

components/VercelChat/tools/googleSheets/GoogleSheetsLoginResult.tsx (1)

11-18: Good refactor to shared component.

The migration to ComposioLoginResult eliminates duplication and centralizes the login UI. The props are well-aligned with the toolkit configuration.

components/VercelChat/tools/googleDrive/GoogleDriveLoginLoading.tsx (1)

1-7: Consistent pattern with GoogleSheetsLoginLoading.

Clean delegation to the shared ComposioLoginLoading component. Follows the established pattern for toolkit-specific loading wrappers.

components/VercelChat/ToolComponents.tsx (3)

106-107: Imports added correctly.

New imports follow the existing import organization pattern.


288-293: Tool call component wiring follows established pattern.

The googleDriveLoginTool case mirrors the googleSheetsLoginTool pattern exactly, maintaining consistency.


559-564: Tool result component wiring follows established pattern.

Consistent with the googleSheetsLoginTool result handling above.

components/VercelChat/tools/googleSheets/GoogleSheetsLoginLoading.tsx (1)

1-7: LGTM! Clean refactoring to shared component.

The refactoring successfully eliminates code duplication by delegating to ComposioLoginLoading. The component maintains a clear single responsibility and follows composition over duplication principles.

components/VercelChat/tools/googleDrive/GoogleDriveLoginResult.tsx (1)

1-22: LGTM! Well-composed login result component.

The component demonstrates excellent separation of concerns: presentation logic is cleanly separated from business logic (delegated to the hook), and shared UI is properly composed via ComposioLoginResult. The use of HardDrive from lucide-react aligns with coding guidelines for application icons.

hooks/useGoogleSheetsLogin.ts (1)

1-8: LGTM! Excellent refactoring to shared hook.

The refactoring successfully eliminates login logic duplication by delegating to the centralized useComposioLogin hook. This demonstrates strong adherence to DRY principles while maintaining a clear, focused API for Google Sheets login.

lib/tools/composio/loginTools.ts (5)

1-4: Clean and minimal imports.

All imports are utilized and properly organized. The use of the @/ alias maintains consistency with the project's module resolution.


6-13: Well-structured input schema with clear validation.

The zod schema provides meaningful validation with a helpful error message that guides the AI on where to source the account_id. This aligns well with the self-documenting code principle.


34-38: Good use of TypeScript for compile-time safety.

The Record<ComposioToolkitKey, Tool> type ensures that if a new toolkit is added to COMPOSIO_TOOLKITS, TypeScript will enforce that this record is updated accordingly. This is a solid approach that catches missing entries at compile time rather than runtime.


40-45: Clean single-responsibility getter.

The function is appropriately minimal and leverages TypeScript's type system to ensure only valid toolkit keys are accepted. No additional validation needed.


47-49: Good backward compatibility strategy.

Exporting named aliases alongside the generic getLoginToolForToolkit function maintains API compatibility for existing consumers while enabling the new toolkit-agnostic approach. This respects the Open/Closed principle by extending functionality without modifying existing interfaces.

import React from "react";
import { LucideIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useUserProvider } from "@/providers/UserProvder";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find provider files with similar names
fd -i "userprov" providers/

Repository: Recoupable-com/Recoup-Chat

Length of output: 95


🏁 Script executed:

# Also check the full path for the file in question
fd -t f "ComposioLoginResult.tsx"

Repository: Recoupable-com/Recoup-Chat

Length of output: 130


🏁 Script executed:

# Search for UserProvider/UserProvder in providers directory
rg "UserProv" providers/ -t ts -t tsx

Repository: Recoupable-com/Recoup-Chat

Length of output: 97


🏁 Script executed:

# Let's read the ComposioLoginResult.tsx file to verify other issues
cat -n components/VercelChat/tools/composio/ComposioLoginResult.tsx

Repository: Recoupable-com/Recoup-Chat

Length of output: 2411


🏁 Script executed:

# Also verify the content of the provider file
cat -n providers/UserProvder.tsx

Repository: Recoupable-com/Recoup-Chat

Length of output: 950


Remove hardcoded "Google" text and use cn() for class merging.

The notice at line 60-62 assumes all toolkits redirect to Google, which breaks reusability for non-Google Composio integrations. Make the redirect service dynamic via props or pass it through context.

Additionally, line 46 uses string concatenation for className instead of the cn() utility per coding guidelines. Replace className={w-full ${buttonColor} text-white} with proper class merging using cn().

Note: The import path UserProvder correctly matches the actual provider filename, though the provider file itself has a typo in its name (UserProvder.tsx should be UserProvider.tsx).

🤖 Prompt for AI Agents
In @components/VercelChat/tools/composio/ComposioLoginResult.tsx at line 6, The
ComposioLoginResult component hardcodes "Google" and uses string-concatenated
classNames; make the redirect service dynamic (add a prop like serviceName or
redirectService or read it from useUserProvider()) and replace the literal
"Google" text with that prop/value throughout the component so non-Google
integrations work; also replace the className usage className={`w-full
${buttonColor} text-white`} with the cn() utility (e.g., cn('w-full',
buttonColor, 'text-white')) to merge classes properly while keeping the existing
import of useUserProvider (whose filename currently has the typo UserProvder)
unchanged.

const [isLoading, setIsLoading] = useState(false);
const { userData } = useUserProvider();
const { messages } = useVercelChatContext();
const accountId = userData?.account_id as string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add null safety for accountId.

The accountId is cast to string without checking if userData?.account_id is defined. This could lead to "undefined" being passed as a string to API calls, causing cryptic errors.

🔒 Proposed fix with early validation
  const { userData } = useUserProvider();
  const { messages } = useVercelChatContext();
- const accountId = userData?.account_id as string;
+ const accountId = userData?.account_id;
  const config = getToolkitConfig(toolkitKey);

  const handleLogin = async () => {
+   if (!accountId) {
+     toast.error("Account information is missing. Please refresh the page.");
+     return;
+   }
+
    const latestUserMessageText = getLatestUserMessageText(messages);

Additionally, consider reflecting this state in the consuming components by including accountId in the return value so buttons can be properly disabled when the account is not available.

🤖 Prompt for AI Agents
In @hooks/useComposioLogin.ts at line 21, The hook useComposioLogin currently
force-casts userData?.account_id to a string via accountId =
userData?.account_id as string; — change this to null-safe handling: validate
that userData and userData.account_id exist (e.g., derive accountId as string |
null or undefined instead of forcing a cast), return that accountId from the
hook, and update any API call paths inside the hook (and consuming components)
to early-return or disable operations when accountId is missing; ensure the
returned shape includes accountId so callers can disable buttons when no account
is available.

Comment on lines +30 to +34
const options: CreateConnectedAccountOptions = {
callbackUrl: `https://chat.recoupable.com?q=${encodeURIComponent(
latestUserMessageText
)}`,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded production URL with environment variable.

The callback URL uses a hardcoded production domain https://chat.recoupable.com, which breaks local development and violates environment-specific configuration practices. Use window.location.origin for client-side code or an environment variable for server-side code.

🔧 Proposed fix using window.location.origin or environment variable

If this code runs in a browser context (client-side):

   const options: CreateConnectedAccountOptions = {
-    callbackUrl: `https://chat.recoupable.com?q=${encodeURIComponent(
+    callbackUrl: `${window.location.origin}?q=${encodeURIComponent(
       latestUserMessageText
     )}`,
   };

If this is server-side code:

+  const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
+
   const options: CreateConnectedAccountOptions = {
-    callbackUrl: `https://chat.recoupable.com?q=${encodeURIComponent(
+    callbackUrl: `${baseUrl}?q=${encodeURIComponent(
       latestUserMessageText
     )}`,
   };
🤖 Prompt for AI Agents
In @lib/agents/composioAgent/getToolkitTools.ts around lines 30 - 34, The
callbackUrl in the CreateConnectedAccountOptions object is using a hardcoded
production domain; update the options object so callbackUrl is built from an
environment-specific origin instead: if this code runs client-side use
window.location.origin, otherwise read a configured environment variable (e.g.,
process.env.APP_ORIGIN) and fall back to a sensible default, then append the
encoded latestUserMessageText as before (refer to options,
CreateConnectedAccountOptions, callbackUrl, latestUserMessageText).

Comment on lines +34 to +37
const [googleSheetsTools, googleDriveTools] = await Promise.all([
getToolkitTools("GOOGLE_SHEETS", body),
getToolkitTools("GOOGLE_DRIVE", body),
]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Consider resilient error handling for toolkit tool fetching.

Using Promise.all means if any single toolkit's tool fetching fails (e.g., network issue, API error), the entire chat request will fail. This reduces resilience compared to allowing the request to proceed with available toolkits.

🛡️ Proposed resilient implementation using Promise.allSettled
-  // Get tools for all Composio toolkits
-  const [googleSheetsTools, googleDriveTools] = await Promise.all([
-    getToolkitTools("GOOGLE_SHEETS", body),
-    getToolkitTools("GOOGLE_DRIVE", body),
-  ]);
+  // Get tools for all Composio toolkits (continue even if one fails)
+  const toolkitResults = await Promise.allSettled([
+    getToolkitTools("GOOGLE_SHEETS", body),
+    getToolkitTools("GOOGLE_DRIVE", body),
+  ]);
+
+  const googleSheetsTools = toolkitResults[0].status === "fulfilled" 
+    ? toolkitResults[0].value 
+    : {};
+  const googleDriveTools = toolkitResults[1].status === "fulfilled" 
+    ? toolkitResults[1].value 
+    : {};

This approach ensures that if one toolkit is temporarily unavailable, users can still chat with other available toolkits.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [googleSheetsTools, googleDriveTools] = await Promise.all([
getToolkitTools("GOOGLE_SHEETS", body),
getToolkitTools("GOOGLE_DRIVE", body),
]);
// Get tools for all Composio toolkits (continue even if one fails)
const toolkitResults = await Promise.allSettled([
getToolkitTools("GOOGLE_SHEETS", body),
getToolkitTools("GOOGLE_DRIVE", body),
]);
const googleSheetsTools = toolkitResults[0].status === "fulfilled"
? toolkitResults[0].value
: {};
const googleDriveTools = toolkitResults[1].status === "fulfilled"
? toolkitResults[1].value
: {};
🤖 Prompt for AI Agents
In @lib/chat/setupToolsForRequest.ts around lines 34 - 37, Replace the
Promise.all call that fetches toolkit tools so a single toolkit failure does not
bail the whole request: call Promise.allSettled with the two
getToolkitTools("GOOGLE_SHEETS", body) and getToolkitTools("GOOGLE_DRIVE", body)
promises, inspect each result, set googleSheetsTools and googleDriveTools to the
fulfilled values (or an empty array/null) when rejected, and log the rejection
errors via your logging utility; ensure subsequent code that uses
googleSheetsTools/googleDriveTools can handle missing/empty values so the
request proceeds with available toolkits.

Comment on lines +28 to +34
if (userAccounts.items.length === 0) {
await authenticateToolkit(toolkitKey, accountId, options);
userAccounts = await composio.connectedAccounts.list({
userIds: [accountId],
toolkitSlugs: [config.slug],
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reconsider immediate retry after OAuth initiation.

The function calls authenticateToolkit and immediately retries listing accounts. However, OAuth authentication is an asynchronous process that requires user interaction in a browser redirect flow. The second list() call will likely still return zero accounts because the OAuth flow hasn't completed yet. This logic appears to assume the authentication completes synchronously, which isn't how OAuth works.

Consider whether this retry is necessary or if the authentication initiation should return a pending state that the caller handles differently. The current implementation may create confusion about when accounts will actually be available.

🤖 Prompt for AI Agents
In @lib/composio/getConnectedAccount.ts around lines 28 - 34,
getConnectedAccount currently calls authenticateToolkit and then immediately
re-invokes composio.connectedAccounts.list, which assumes OAuth completes
synchronously; instead, stop retrying immediately after authenticateToolkit.
Modify getConnectedAccount so that after calling authenticateToolkit it returns
a clear pending/auth-initiated result (or throws a specific AuthPending error)
that the caller can handle (e.g., trigger browser flow and poll later), or
alternatively document that the caller must re-run/get accounts after the OAuth
flow completes; do not call composio.connectedAccounts.list again directly after
authenticateToolkit. Ensure references to authenticateToolkit,
composio.connectedAccounts.list, and the userAccounts handling are updated
accordingly.

Comment on lines +30 to +34
const options: CreateConnectedAccountOptions = {
callbackUrl: `https://chat.recoupable.com?q=${encodeURIComponent(
latestUserMessageText
)}`,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback URL is hardcoded to the production domain, which breaks OAuth redirect handling in development environments and is inconsistent with the client-side hook implementation.

View Details
📝 Patch Details
diff --git a/.env.example b/.env.example
index 1012a5c0..f5ead872 100644
--- a/.env.example
+++ b/.env.example
@@ -24,4 +24,6 @@ NEXT_PUBLIC_SPLASH_BACKGROUND_COLOR=FFFFFF
 
 # COMPOSIO
 COMPOSIO_API_KEY=
-COMPOSIO_GOOGLE_SHEETS_AUTH_CONFIG_ID=
\ No newline at end of file
+COMPOSIO_GOOGLE_SHEETS_AUTH_CONFIG_ID=
+COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID=
+NEXT_PUBLIC_CHAT_BASE_URL=
\ No newline at end of file
diff --git a/lib/agents/composioAgent/getToolkitTools.ts b/lib/agents/composioAgent/getToolkitTools.ts
index 6fb78296..2179aca2 100644
--- a/lib/agents/composioAgent/getToolkitTools.ts
+++ b/lib/agents/composioAgent/getToolkitTools.ts
@@ -9,6 +9,7 @@ import {
 } from "@/lib/composio/getConnectedAccount";
 import { ComposioToolkitKey, getToolkitConfig } from "@/lib/composio/toolkits";
 import { getLoginToolForToolkit } from "@/lib/tools/composio/loginTools";
+import { getCallbackUrl } from "@/lib/composio/getCallbackUrl";
 
 /**
  * Get tools for a Composio toolkit.
@@ -28,9 +29,7 @@ export async function getToolkitTools(
   const latestUserMessageText = getLatestUserMessageText(messages);
 
   const options: CreateConnectedAccountOptions = {
-    callbackUrl: `https://chat.recoupable.com?q=${encodeURIComponent(
-      latestUserMessageText
-    )}`,
+    callbackUrl: getCallbackUrl(latestUserMessageText),
   };
 
   const composio = await getComposioClient();
diff --git a/lib/composio/getCallbackUrl.ts b/lib/composio/getCallbackUrl.ts
new file mode 100644
index 00000000..de69fcf9
--- /dev/null
+++ b/lib/composio/getCallbackUrl.ts
@@ -0,0 +1,19 @@
+/**
+ * Get the callback URL for Composio OAuth.
+ * Uses environment variable for the base URL to support dev and production environments.
+ *
+ * @param query - Optional query parameter (e.g., the latest user message text)
+ * @returns The callback URL for Composio OAuth redirects
+ */
+export function getCallbackUrl(query?: string): string {
+  // Use environment variable for base URL (set in .env or deployment config)
+  // This supports development (localhost:3000) and production (chat.recoupable.com)
+  const baseUrl =
+    process.env.NEXT_PUBLIC_CHAT_BASE_URL || "https://chat.recoupable.com";
+
+  if (query) {
+    return `${baseUrl}?q=${encodeURIComponent(query)}`;
+  }
+
+  return baseUrl;
+}

Analysis

Hardcoded production domain in Composio OAuth callback breaks development environments

What fails: getToolkitTools() in lib/agents/composioAgent/getToolkitTools.ts hardcodes the OAuth callback URL to https://chat.recoupable.com, preventing proper OAuth redirect handling in development environments where the application runs on http://localhost:3000.

How to reproduce:

  1. Set up local development environment (running on localhost:3000)
  2. Attempt to authenticate with Google Sheets or Google Drive via Composio
  3. When setupToolsForRequest() is called server-side, it initiates OAuth with the hardcoded production domain as the callback URL
  4. After OAuth completes, the user is redirected to the production domain instead of localhost

What happens vs expected:

  • Current behavior: Server-side getToolkitTools() stores https://chat.recoupable.com as the OAuth callback destination
  • Expected behavior: Should use the current environment's base URL (from NEXT_PUBLIC_CHAT_BASE_URL env variable or fallback to production), matching the client-side useComposioLogin.ts which correctly uses window.location.origin

Evidence:

  • Commit 408ff1e explicitly states: "Use window.location.origin for dev/prod redirect compatibility" when introducing the client-side useComposioLogin hook
  • The client-side code correctly implements this with: const baseUrl = typeof window !== "undefined" ? window.location.origin : "https://chat.recoupable.com"
  • The server-side code was NOT updated to match, leaving it with a hardcoded production URL
  • This creates an inconsistency where server-side OAuth initiation could interfere with the client-side dynamic flow

Fix implemented: Created lib/composio/getCallbackUrl.ts utility function that uses NEXT_PUBLIC_CHAT_BASE_URL environment variable to dynamically construct the callback URL, supporting both development and production environments. Updated getToolkitTools() to use this utility instead of hardcoding the domain.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY principle - This component should already exist from google sheets login.

  • why are you recreating existing components?

Comment on lines +26 to +27
// Use current origin so it works in both local dev and production
const baseUrl = typeof window !== "undefined" ? window.location.origin : "https://chat.recoupable.com";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KISS principle - why not just always use https://chat.recoupable.com?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KISS principle - is this hook really necessary?

  • why not just directly import useComposioLogin wherever you're using useGoogleDriveLogin?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KISS principle - is this hook really necessary?

  • why not just directly import useComposioLogin wherever you're using useGoogleSheetsLogin?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incorrect file location

  • actual: lib/agents
  • required: lib/composio

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, this should be moved server side in the recoup-api repo.

*/
export async function authenticateToolkit(
toolkitKey: ComposioToolkitKey,
userId: string,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
userId: string,
accountId: string,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants