Skip to content

Conversation

@ngoiyaeric
Copy link
Collaborator

@ngoiyaeric ngoiyaeric commented Dec 30, 2025

User description

Add Manus AI Integration as Specialized Tool

Overview

This PR integrates Manus AI into QCX as a specialized tool, enabling the Researcher Agent to delegate complex, multi-step tasks to the Manus AI platform for advanced reasoning, research, code execution, and workflow automation.

Architecture Decision

The integration follows a tool-based architecture rather than creating a separate agent. This approach was chosen because:

  • Consistency: Aligns with existing QCX patterns (search, retrieve, geospatial tools)
  • Simplicity: Minimal changes to existing routing logic
  • Flexibility: Researcher Agent can intelligently decide when Manus is appropriate
  • Maintainability: Follows established patterns in the codebase
  • Cost Efficiency: Avoids unnecessary LLM calls for routing decisions

Based on the AI SDK workflow patterns documentation, this is the recommended approach for starting with the simplest solution that meets requirements.

Changes

New Files

  1. lib/schema/manus.tsx - Zod schema for Manus tool parameters

    • Validates prompt, agentProfile, taskMode, and interactiveMode
    • Provides TypeScript types for type safety
  2. lib/agents/tools/manus.tsx - Manus tool implementation

    • Handles API calls to Manus AI platform (https://api.manus.ai/v1/tasks)
    • Manages error handling and UI updates
    • Creates tasks and returns task information with links
  3. components/manus-section.tsx - UI component for displaying Manus task results

    • Shows task title, ID, and status
    • Provides links to view task progress and share results
    • Follows QCX design patterns with proper styling
  4. MANUS_INTEGRATION.md - Comprehensive documentation

    • Architecture decisions and rationale
    • Configuration instructions
    • Usage examples and API reference
    • Troubleshooting guide

Modified Files

  1. lib/agents/tools/index.tsx - Tool registry

    • Added Manus tool import
    • Conditionally registers Manus tool when MANUS_API_KEY is present
  2. .env.local.example - Environment configuration

    • Added MANUS_API_KEY configuration with documentation link

Configuration

Users need to add the following to their .env.local file:

# Manus AI API Key
# Get your API key from https://open.manus.im/docs/quickstart
MANUS_API_KEY="your_manus_api_key_here"

Tool Capabilities

The Manus tool is designed for:

  • Complex multi-step workflows requiring planning
  • Deep research across multiple sources
  • Code execution and file manipulation
  • Advanced reasoning and problem-solving
  • Tasks beyond simple search and retrieval

Parameters

  • prompt (required): Task instruction for Manus
  • agentProfile (optional): manus-1.6 (default), manus-1.6-lite, or manus-1.6-max
  • taskMode (optional): chat, adaptive, or agent
  • interactiveMode (optional): Enable follow-up questions (default: false)

Testing

  • ✅ Linting passed with no errors
  • ✅ TypeScript compilation successful
  • ✅ Production build completed successfully
  • ✅ All existing tests pass
  • ✅ Code follows QCX patterns and conventions

Future Enhancements

Potential improvements for future iterations:

  1. Task result polling to fetch and display results directly in QCX
  2. Webhook integration for real-time task completion notifications
  3. Support for Manus connectors (Gmail, Notion, Google Calendar)
  4. File attachment support for Manus tasks
  5. Multi-turn conversation support for continuing existing tasks

Documentation

See MANUS_INTEGRATION.md for:

  • Detailed architecture explanation
  • Complete API reference
  • Usage examples
  • Troubleshooting guide
  • Links to relevant resources

References

Checklist

  • Code follows project style guidelines
  • TypeScript types are properly defined
  • Zod schemas validate all inputs
  • Error handling is comprehensive
  • UI components follow existing patterns
  • Environment variables are documented
  • Integration documentation is complete
  • All tests pass
  • Build succeeds without errors

PR Type

Enhancement


Description

  • Integrates Manus AI as specialized tool for complex multi-step tasks

  • Adds Zod schema validation for Manus tool parameters

  • Creates ManusSection UI component for displaying task results

  • Conditionally registers Manus tool when API key is configured

  • Includes comprehensive integration documentation and setup guide


Diagram Walkthrough

flowchart LR
  A["Researcher Agent"] -->|delegates complex tasks| B["Manus Tool"]
  B -->|validates params| C["Manus Schema"]
  B -->|API call| D["Manus AI Platform"]
  D -->|task created| E["Task Response"]
  E -->|displays| F["ManusSection Component"]
  F -->|shows progress| G["User Interface"]
Loading

File Walkthrough

Relevant files
Enhancement
manus.tsx
Manus tool parameter validation schema                                     

lib/schema/manus.tsx

  • Defines Zod schema for Manus tool parameter validation
  • Validates prompt, agentProfile, taskMode, and interactiveMode fields
  • Provides TypeScript type definitions for type safety
  • Sets default values for agentProfile and interactiveMode
+26/-0   
manus.tsx
Manus tool implementation with API integration                     

lib/agents/tools/manus.tsx

  • Implements Manus tool with API integration to Manus AI platform
  • Handles task creation via POST request to
    https://api.manus.ai/v1/tasks
  • Manages error handling with detailed error messages and UI updates
  • Returns task information including ID, title, and URLs for progress
    tracking
  • Shows loading state during task execution
+108/-0 
manus-section.tsx
UI component for Manus task result display                             

components/manus-section.tsx

  • Creates React component for displaying Manus task results
  • Shows task title, ID, and status information in a Card component
  • Provides external links to view task progress and share results
  • Follows QCX design patterns with proper styling and accessibility
  • Includes informational text about real-time progress tracking
+68/-0   
index.tsx
Tool registry update with Manus integration                           

lib/agents/tools/index.tsx

  • Imports manusTool from the new manus module
  • Conditionally registers Manus tool when MANUS_API_KEY environment
    variable is present
  • Maintains consistent tool registration pattern with other tools
  • Passes uiStream and fullResponse props to manusTool
+8/-0     
Configuration changes
.env.local.example
Environment configuration for Manus API key                           

.env.local.example

  • Adds MANUS_API_KEY configuration variable with documentation
  • Includes link to Manus API quickstart guide for obtaining API key
  • Placed in AI Provider API Keys section alongside other API keys
+4/-0     
Documentation
MANUS_INTEGRATION.md
Complete Manus integration documentation and guide             

MANUS_INTEGRATION.md

  • Comprehensive documentation of Manus AI integration architecture and
    design decisions
  • Detailed configuration instructions and environment setup guide
  • Complete API reference with request/response schemas
  • Usage examples and tool parameter descriptions
  • Testing procedures and troubleshooting guide
  • Future enhancement suggestions and relevant resource links
+187/-0 

Summary by CodeRabbit

  • New Features

    • Manus AI integration for the Researcher Agent: create/run Manus tasks with selectable agent profiles, task modes, and interactive settings; results show task title, ID, progress link, and optional public share link.
  • Documentation

    • Added detailed Manus integration guide covering setup, usage, configuration, testing, and troubleshooting.
  • Chores

    • Example environment file updated with MANUS_API_KEY entry and brief instruction.

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

- Add Manus tool with Zod schema for parameter validation
- Create ManusSection UI component for displaying task results
- Register Manus tool conditionally based on MANUS_API_KEY
- Add MANUS_API_KEY to environment configuration
- Include comprehensive documentation in MANUS_INTEGRATION.md

The integration follows QCX's existing tool-based architecture,
allowing the Researcher Agent to delegate complex multi-step tasks
to the Manus AI platform for advanced reasoning, research, and
workflow automation.
@vercel
Copy link

vercel bot commented Dec 30, 2025

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

Project Deployment Review Updated (UTC)
qcx Ready Ready Preview, Comment Dec 30, 2025 4:28pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Walkthrough

Adds Manus AI integration: environment variable example, Zod schema, a new Manus tool implementing POST /v1/tasks with validation, UI component to display task results, and conditional registration of the tool when MANUS_API_KEY is present.

Changes

Cohort / File(s) Summary
Configuration
\.env.local.example``
Added MANUS_API_KEY="your_manus_api_key_here" and a brief instruction comment.
Schema & Types
lib/schema/manus.tsx
New Zod schema manusSchema and exported types ManusInput and PartialManus describing prompt, agentProfile, taskMode, and interactiveMode.
Tool Implementation
lib/agents/tools/manus.tsx
New manusTool export: validates API key, builds request body, POSTs to https://api.manus.ai/v1/tasks with 30s abort, validates response against manusResponseSchema, validates URLs, sanitizes errors, updates UI (SearchSkeleton / Card), logs non-sensitive audit entry, returns taskResult or null.
Tool Registration
lib/agents/tools/index.tsx
Imports manusTool and conditionally exposes tools.manusTask when process.env.MANUS_API_KEY is set; existing tools left unchanged.
UI Component
components/manus-section.tsx
New ManusSection React component rendering task title, task ID, link to task progress and optional public share link, with descriptive note.
Documentation
MANUS_INTEGRATION.md
New integration doc describing architecture, implementation details, configuration (MANUS_API_KEY), usage, API reference, security and testing notes, and created/modified files.

Sequence Diagram(s)

sequenceDiagram
    actor ResearcherAgent as Researcher Agent
    participant Tool as Manus Tool
    participant API as Manus API
    participant UI as UI Stream
    participant Component as ManusSection

    ResearcherAgent->>Tool: execute(manusTool, params)
    activate Tool

    rect rgb(240,248,255)
    note right of Tool: Init UI & validate config
    Tool->>UI: render(SearchSkeleton)
    Tool->>Tool: check process.env.MANUS_API_KEY
    end

    rect rgb(240,255,240)
    note right of Tool: API request (abortable)
    Tool->>API: POST /v1/tasks (body: prompt, agentProfile, taskMode, interactiveMode)
    alt API 200
        API-->>Tool: { task_id, task_title, task_url, [share_url] }
        Tool->>Tool: validate response schema and URLs
    else API error / non-OK
        API-->>Tool: error
        Tool->>Tool: sanitize error
    end
    end

    rect rgb(255,250,240)
    note right of UI: UI update
    alt success
        Tool->>Component: pass taskResult
        UI->>Component: render ManusSection
        Tool->>ResearcherAgent: return taskResult
    else failure
        Tool->>UI: render error Card (sanitized)
        Tool->>ResearcherAgent: return null
    end
    end

    deactivate Tool
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

Possible security concern

Suggested reviewers

  • CharlieHelps

Poem

🐰 A rabbit scribbles in the margins:
Manus arrives, keys set just right,
Tasks take flight through day and night,
Schemas snug, responses neat,
UI hums — the workflow's sweet! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Manus AI as a specialized tool to the QCX platform.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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.

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 30, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Sensitive error disclosure

Description: The error handling appends and renders raw upstream Manus API response text (errorText /
errorMessage) into fullResponse and the UI, which could leak sensitive details (e.g.,
request identifiers, account metadata, or other confidential diagnostics) returned by the
third-party service and potentially enable information disclosure to end users.
manus.tsx [56-80]

Referred Code
  if (!response.ok) {
    const errorText = await response.text()
    throw new Error(
      `Manus API error: ${response.status} - ${errorText}`
    )
  }

  taskResult = await response.json()

  if (!taskResult) {
    hasError = true
  }
} catch (error) {
  hasError = true
  console.error('Manus API error:', error)

  const errorMessage =
    error instanceof Error ? error.message : 'Unknown error occurred'
  fullResponse += `\nAn error occurred while executing Manus task: ${errorMessage}`

  uiStream.update(


 ... (clipped 4 lines)
Untrusted link injection

Description: Untrusted URLs returned by the Manus API (data.task_url / data.share_url) are directly
used as href targets without scheme/host allowlisting, enabling malicious
javascript:/data: URLs or phishing links if the upstream response is compromised or
spoofed.
manus-section.tsx [36-56]

Referred Code
<a
  href={data.task_url}
  target="_blank"
  rel="noopener noreferrer"
  className="inline-flex items-center gap-2 text-sm text-primary hover:underline"
>
  <ExternalLink className="w-4 h-4" />
  View Task Progress
</a>

{data.share_url && (
  <a
    href={data.share_url}
    target="_blank"
    rel="noopener noreferrer"
    className="inline-flex items-center gap-2 text-sm text-primary hover:underline"
  >
    <ExternalLink className="w-4 h-4" />
    Public Share Link
  </a>
)}
Public link data exposure

Description: The tool always requests createShareableLink: true, which may unintentionally create
publicly accessible share links for user prompts/results and lead to inadvertent data
exposure depending on what content is sent to Manus and how share links are permissioned.
manus.tsx [47-53]

Referred Code
body: JSON.stringify({
  prompt,
  agentProfile,
  taskMode,
  interactiveMode,
  createShareableLink: true
})
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Leaky user errors: The UI and fullResponse include raw upstream error details (status and errorText), which
can expose internal/service information to end users instead of a generic user-facing
message.

Referred Code
  if (!response.ok) {
    const errorText = await response.text()
    throw new Error(
      `Manus API error: ${response.status} - ${errorText}`
    )
  }

  taskResult = await response.json()

  if (!taskResult) {
    hasError = true
  }
} catch (error) {
  hasError = true
  console.error('Manus API error:', error)

  const errorMessage =
    error instanceof Error ? error.message : 'Unknown error occurred'
  fullResponse += `\nAn error occurred while executing Manus task: ${errorMessage}`

  uiStream.update(


 ... (clipped 4 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logging: The new external task creation action (POST to Manus) is not accompanied by an audit log
containing actor context (e.g., user ID), making it unclear whether this critical action
is traceable in system audit trails.

Referred Code
// Create task
const response = await fetch('https://api.manus.ai/v1/tasks', {
  method: 'POST',
  headers: {
    accept: 'application/json',
    'content-type': 'application/json',
    API_KEY: apiKey
  },
  body: JSON.stringify({
    prompt,
    agentProfile,
    taskMode,
    interactiveMode,
    createShareableLink: true
  })
})

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unbounded error body: The error path reads and surfaces the full response body via response.text() without
size/format guarding, which can create poor UX and operational risk if the upstream
returns unexpectedly large or non-text error payloads.

Referred Code
if (!response.ok) {
  const errorText = await response.text()
  throw new Error(
    `Manus API error: ${response.status} - ${errorText}`
  )
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Potential sensitive logs: console.error('Manus API error:', error) may log upstream response details and
request-associated context depending on the thrown error, so it is unclear whether
sensitive data could end up in logs without explicit redaction.

Referred Code
} catch (error) {
  hasError = true
  console.error('Manus API error:', error)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated external response: The code trusts await response.json() as taskResult without validating it against a schema
before rendering and inserting into fullResponse, which may allow malformed/unexpected
upstream data to propagate.

Referred Code
  taskResult = await response.json()

  if (!taskResult) {
    hasError = true
  }
} catch (error) {
  hasError = true
  console.error('Manus API error:', error)

  const errorMessage =
    error instanceof Error ? error.message : 'Unknown error occurred'
  fullResponse += `\nAn error occurred while executing Manus task: ${errorMessage}`

  uiStream.update(
    <Card className="p-4 mt-2 text-sm">
      {`An error occurred while executing Manus task: ${errorMessage}`}
    </Card>
  )
  return null
}



 ... (clipped 20 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 30, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Integration lacks result retrieval mechanism

The current Manus AI integration only initiates tasks without retrieving their
results. To enable the agent to use the output for further actions, a result
retrieval mechanism like polling or webhooks should be implemented.

Examples:

lib/agents/tools/manus.tsx [11-107]
  execute: async ({
    prompt,
    agentProfile,
    taskMode,
    interactiveMode
  }: {
    prompt: string
    agentProfile: 'manus-1.6' | 'manus-1.6-lite' | 'manus-1.6-max'
    taskMode?: 'chat' | 'adaptive' | 'agent'
    interactiveMode: boolean

 ... (clipped 87 lines)

Solution Walkthrough:

Before:

// lib/agents/tools/manus.tsx
async function execute(params) {
  // ... show loading UI
  
  // Call Manus API to CREATE a task
  const response = await fetch('https://api.manus.ai/v1/tasks', { ... });
  const taskResult = await response.json(); // Contains task_id, task_url

  // Update UI to show a link to the task on Manus's platform
  uiStream.update(<ManusSection data={taskResult} />);

  // Return task metadata, NOT the actual result of the task
  return taskResult;
}

After:

// lib/agents/tools/manus.tsx
async function execute(params) {
  // 1. Call Manus API to CREATE a task
  const response = await fetch('https://api.manus.ai/v1/tasks', { ... });
  const taskCreationResult = await response.json();

  // 2. Implement a polling mechanism to check task status
  let taskFinalResult = null;
  while (true) {
    const statusResponse = await fetch(`https://api.manus.ai/v1/tasks/${taskCreationResult.task_id}`);
    const statusData = await statusResponse.json();
    if (statusData.status === 'completed') {
      taskFinalResult = statusData.output;
      break;
    }
    await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5s
  }

  // 3. Update UI and return the actual result to the agent
  uiStream.update(<ResultComponent data={taskFinalResult} />);
  return taskFinalResult;
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical design limitation where the agent cannot retrieve task results, which severely limits the feature's utility for multi-step reasoning and makes the integration a 'fire-and-forget' mechanism.

High
Possible issue
Use uiStream.append instead of string mutation
Suggestion Impact:The commit removed the success-message `fullResponse += ...` block entirely (and also removed the error-path `fullResponse += ...` mutation), relying on UI updates (`uiStream.update()` / error Card) instead. While it did not implement the suggested `uiStream.append(...)` success text verbatim, it addressed the core issue by eliminating the ineffective `fullResponse` string mutation.

code diff:

-      const errorMessage =
-        error instanceof Error ? error.message : 'Unknown error occurred'
-      fullResponse += `\nAn error occurred while executing Manus task: ${errorMessage}`
+      const sanitizedMessage = sanitizeErrorMessage(error)
 
       uiStream.update(
         <Card className="p-4 mt-2 text-sm">
-          {`An error occurred while executing Manus task: ${errorMessage}`}
+          {sanitizedMessage}
         </Card>
       )
+      
       return null
     }
-
-    if (hasError || !taskResult) {
-      fullResponse += `\nAn error occurred while executing the Manus task.`
-      uiStream.update(
-        <Card className="p-4 mt-2 text-sm">
-          An error occurred while executing the Manus task.
-        </Card>
-      )
-      return null
-    }
-
-    // Update UI with task result
-    uiStream.update(<ManusSection data={taskResult} />)
-
-    // Add task information to the response context
-    fullResponse += `\n\nManus task created successfully:
-- Task ID: ${taskResult.task_id}
-- Title: ${taskResult.task_title}
-- Task URL: ${taskResult.task_url}
-${taskResult.share_url ? `- Share URL: ${taskResult.share_url}` : ''}
-
-You can view the task progress and results at the provided URL.`
-
-    return taskResult
   }

Use uiStream.append() to display the task success message, as mutating the
fullResponse string has no effect outside the function.

lib/agents/tools/manus.tsx [98-104]

-fullResponse += `
+uiStream.append(`
 
 Manus task created successfully:
 - Task ID: ${taskResult.task_id}
 - Title: ${taskResult.task_title}
 - Task URL: ${taskResult.task_url}
 ${taskResult.share_url ? `- Share URL: ${taskResult.share_url}` : ''}
 
-You can view the task progress and results at the provided URL.`
+You can view the task progress and results at the provided URL.`);

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that modifying fullResponse via += will not work as it's a string passed by value. This is a valid bug in the logic. While the proposed fix of using uiStream.append() might not align with the intended purpose of fullResponse (a text log), it correctly points out a flaw and offers a way to present the information to the user.

Medium
General
Filter undefined fields and add fetch timeout
Suggestion Impact:The commit refactors the Manus API call to (1) construct a requestBody object that conditionally includes optional fields only when defined, and (2) add an AbortController-based 30s timeout passed via fetch's `signal`, clearing the timeout afterward. The implementation also adds extra hardening (response validation, error sanitization), but the core suggested changes are present.

code diff:

+      // Filter undefined fields from request body
+      const requestBody: Record<string, unknown> = {
+        prompt: params.prompt,
+        agentProfile: params.agentProfile
       }
 
-      taskResult = await response.json()
+      if (params.taskMode !== undefined) {
+        requestBody.taskMode = params.taskMode
+      }
 
-      if (!taskResult) {
-        hasError = true
+      if (params.interactiveMode !== undefined) {
+        requestBody.interactiveMode = params.interactiveMode
+      }
+
+      // Only create shareable link if explicitly requested
+      // For now, default to false for privacy
+      requestBody.createShareableLink = false
+
+      // Create AbortController for timeout
+      const controller = new AbortController()
+      const timeoutId = setTimeout(() => controller.abort(), 30000) // 30 second timeout
+
+      try {
+        // Create task
+        const response = await fetch('https://api.manus.ai/v1/tasks', {
+          method: 'POST',
+          headers: {
+            accept: 'application/json',
+            'content-type': 'application/json',
+            API_KEY: apiKey
+          },
+          body: JSON.stringify(requestBody),
+          signal: controller.signal
+        })
+
+        clearTimeout(timeoutId)
+
+        if (!response.ok) {
+          // Don't expose raw error response
+          throw new Error(`API request failed with status ${response.status}`)
+        }
+
+        const rawData = await response.json()
+
+        // Validate response structure
+        const taskResult = manusResponseSchema.parse(rawData)
+
+        // Validate URLs before using them
+        if (!isValidUrl(taskResult.task_url)) {
+          throw new Error('Invalid task URL received from API')
+        }
+
+        if (taskResult.share_url && !isValidUrl(taskResult.share_url)) {
+          // Remove invalid share URL rather than failing
+          taskResult.share_url = undefined
+        }
+
+        // Update UI with task result
+        uiStream.update(<ManusSection data={taskResult} />)
+
+        // Log task creation for audit trail (without sensitive data)
+        console.info('Manus task created', {
+          task_id: taskResult.task_id,
+          timestamp: new Date().toISOString()
+        })
+
+        return taskResult
+      } catch (fetchError) {
+        clearTimeout(timeoutId)
+        if (fetchError instanceof Error && fetchError.name === 'AbortError') {
+          throw new Error('Request timeout. The API took too long to respond.')
+        }
+        throw fetchError
       }

Refactor the API call to include a timeout using an AbortController and
explicitly build the payload to exclude undefined optional fields.

lib/agents/tools/manus.tsx [40-54]

+const controller = new AbortController()
+const timeout = setTimeout(() => controller.abort(), 30000)
+const payload: Record<string, any> = {
+  prompt,
+  agentProfile,
+  interactiveMode,
+  createShareableLink: true
+}
+if (taskMode) payload.taskMode = taskMode
 const response = await fetch('https://api.manus.ai/v1/tasks', {
   method: 'POST',
   headers: {
     accept: 'application/json',
     'content-type': 'application/json',
     API_KEY: apiKey
   },
-  body: JSON.stringify({
-    prompt,
-    agentProfile,
-    taskMode,
-    interactiveMode,
-    createShareableLink: true
-  })
+  body: JSON.stringify(payload),
+  signal: controller.signal
 })
+clearTimeout(timeout)

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly proposes adding a timeout to the fetch call, which is a best practice for resilience against network issues and prevents the application from hanging. Explicitly handling optional fields before stringifying the payload also improves code clarity and robustness.

Medium
Add aria-label for accessibility
Suggestion Impact:The commit added an aria-label to the "View Task Progress" link (using a dynamic label including the task title) and also improved accessibility further by marking the ExternalLink icon as aria-hidden and adding a similar aria-label to the "Public Share Link".

code diff:

@@ -37,9 +37,10 @@
             href={data.task_url}
             target="_blank"
             rel="noopener noreferrer"
+            aria-label={`View progress for task: ${data.task_title}`}
             className="inline-flex items-center gap-2 text-sm text-primary hover:underline"
           >
-            <ExternalLink className="w-4 h-4" />
+            <ExternalLink className="w-4 h-4" aria-hidden="true" />
             View Task Progress
           </a>
 
@@ -48,9 +49,10 @@
               href={data.share_url}
               target="_blank"
               rel="noopener noreferrer"
+              aria-label={`Public share link for task: ${data.task_title}`}
               className="inline-flex items-center gap-2 text-sm text-primary hover:underline"
             >
-              <ExternalLink className="w-4 h-4" />
+              <ExternalLink className="w-4 h-4" aria-hidden="true" />
               Public Share Link
             </a>

Add an aria-label="View Manus task progress" to the "View Task Progress" link to
improve accessibility for screen readers.

components/manus-section.tsx [36-44]

 <a
   href={data.task_url}
   target="_blank"
   rel="noopener noreferrer"
+  aria-label="View Manus task progress"
   className="inline-flex items-center gap-2 text-sm text-primary hover:underline"
 >
   <ExternalLink className="w-4 h-4" />
   View Task Progress
 </a>

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why: The suggestion improves accessibility by adding an aria-label, which provides a more descriptive context for screen reader users. This is a good practice for creating more inclusive user interfaces.

Low
  • Update

@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai how do we run the playwright testing

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

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13697f3 and 93b5d41.

📒 Files selected for processing (6)
  • .env.local.example
  • MANUS_INTEGRATION.md
  • components/manus-section.tsx
  • lib/agents/tools/index.tsx
  • lib/agents/tools/manus.tsx
  • lib/schema/manus.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
lib/agents/tools/manus.tsx (2)
lib/schema/manus.tsx (1)
  • manusSchema (4-24)
components/search-skeleton.tsx (1)
  • SearchSkeleton (6-30)
lib/agents/tools/index.tsx (1)
lib/agents/tools/manus.tsx (1)
  • manusTool (7-108)
🪛 dotenv-linter (4.0.0)
.env.local.example

[warning] 16-16: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)

🪛 LanguageTool
MANUS_INTEGRATION.md

[style] ~70-~70: Consider a different adjective to strengthen your wording.
Context: ...uire: - Complex multi-step workflows - Deep research across multiple sources - Code...

(DEEP_PROFOUND)

🔇 Additional comments (7)
.env.local.example (1)

14-16: LGTM!

The Manus API key entry follows the established pattern in this file. The static analysis warning about quote characters is a false positive since all placeholder values in this example file use the same format.

lib/schema/manus.tsx (1)

4-24: LGTM!

The Zod schema is well-structured with clear descriptions for each field. The enum values, defaults, and optional fields are appropriately defined.

components/manus-section.tsx (1)

15-66: LGTM!

The component is well-structured with proper security attributes on external links (rel="noopener noreferrer"). The conditional rendering for the optional share_url and the clear UI hierarchy follow good React patterns.

lib/agents/tools/index.tsx (1)

39-44: LGTM!

The conditional registration of the Manus tool follows the established pattern used by videoSearchTool. The environment variable check ensures the tool is only available when properly configured.

lib/agents/tools/manus.tsx (2)

7-10: LGTM!

The tool description clearly explains Manus capabilities, and the schema integration provides proper parameter validation.


42-46: The header API_KEY is correct and matches the Manus API documentation — no changes needed. The Manus API expects authentication via the API_KEY header with the raw key as the value, which is exactly how it's implemented here.

MANUS_INTEGRATION.md (1)

1-187: LGTM!

The documentation is comprehensive, well-organized, and accurately reflects the implementation. It covers architecture decisions, configuration, usage, API reference, and troubleshooting effectively.

Comment on lines +22 to +24
let hasError = false
// Show loading state
uiStream.append(<SearchSkeleton />)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider a more specific loading skeleton.

SearchSkeleton is reused for loading state, but Manus tasks may take significantly longer than search operations. Consider adding text indicating a longer operation or using a different skeleton that sets appropriate expectations.

🤖 Prompt for AI Agents
In lib/agents/tools/manus.tsx around lines 22 to 24, the component uses a
generic SearchSkeleton for loading but Manus tasks are longer-running; replace
or augment the skeleton with a Manus-specific loading UI (e.g., a
longer-operation skeleton or one with explanatory text and progress indicator)
so users understand this is a lengthy task. Update the uiStream.append call to
render the new ManusLoading/LongRunningSkeleton component or to render
SearchSkeleton plus a descriptive message like "Processing manuscript — this may
take several minutes" and, if available, add an indeterminate progress spinner
or estimated time text.


const errorMessage =
error instanceof Error ? error.message : 'Unknown error occurred'
fullResponse += `\nAn error occurred while executing Manus task: ${errorMessage}`
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:

# First, let's check the structure of manus.tsx around the mentioned lines
cat -n lib/agents/tools/manus.tsx | head -150 | tail -100

Repository: QueueLab/QCX

Length of output: 3650


🏁 Script executed:

# Also run the provided verification script to see how fullResponse is used
rg -n "fullResponse" lib/agents/tools/ -A2 -B2

Repository: QueueLab/QCX

Length of output: 5567


String mutations on fullResponse have no effect and are silently discarded.

fullResponse is passed as a parameter of type string (ToolProps, line 10 in index.tsx). The += operations on lines 74, 85, and 98–104 create new local strings without updating the original variable. Since the function returns taskResult instead of the modified fullResponse, the accumulated response content is lost to the caller.

This pattern exists across all tools (searchTool, retrieveTool, videoSearchTool, manusTool). Either return the updated fullResponse string or use a mutable wrapper object to propagate response updates to the caller.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Check if Playwright configuration exists
echo "=== Searching for Playwright configuration files ==="
fd -t f "playwright.config" --exec ls -lh

# Check package.json for test scripts
echo -e "\n=== Checking package.json for test scripts ==="
if [ -f "package.json" ]; then
  cat package.json | jq '.scripts | to_entries | .[] | select(.key | contains("test") or contains("playwright") or contains("e2e"))'
fi

# Look for Playwright test files
echo -e "\n=== Searching for Playwright test files ==="
fd -e spec.ts -e spec.js -e test.ts -e test.js --type f | head -20

# Check if there's a tests or e2e directory
echo -e "\n=== Checking for test directories ==="
fd -t d "test|e2e|playwright" --max-depth 2

# Check if playwright is installed
echo -e "\n=== Checking if Playwright is in dependencies ==="
if [ -f "package.json" ]; then
  cat package.json | jq '{devDependencies: .devDependencies | with_entries(select(.key | contains("playwright"))), dependencies: .dependencies | with_entries(select(.key | contains("playwright")))}'
fi

Length of output: 824


🏁 Script executed:

#!/bin/bash

# Let's also check README for testing instructions
echo "=== Checking README for testing instructions ==="
if [ -f "README.md" ]; then
  rg -i "test|playwright" README.md -C 2
fi

# Check for any documentation about testing
echo -e "\n=== Searching for testing documentation ==="
fd -t f "TESTING|TEST" --case-sensitive

# Let's look at the full package.json scripts section
echo -e "\n=== Full scripts section from package.json ==="
if [ -f "package.json" ]; then
  cat package.json | jq '.scripts'
fi

Length of output: 491


🏁 Script executed:

#!/bin/bash

# Check the Playwright configuration for any special requirements
echo "=== Playwright Configuration ==="
cat playwright.config.ts

Length of output: 1009


@ngoiyaeric Here's how to run Playwright testing in the QCX repository:

Prerequisites

Start the development server first (the webServer config is commented out in playwright.config.ts):

npm run dev
# or
bun dev

Keep this running in a separate terminal.

Running Tests

Once the dev server is running on http://localhost:3000, you have several options:

1. Run all tests (headless mode)

npm run test:e2e

This runs all tests in the background across all configured browsers (Chromium, Firefox, WebKit, Mobile Chrome, Mobile Safari).

2. Run tests with UI mode (Recommended for development)

npm run test:e2e:ui

Opens Playwright's interactive UI where you can:

  • Run individual tests
  • Watch tests execute
  • Time-travel through test steps
  • Debug failures

3. Run tests in headed mode

npm run test:e2e:headed

Runs tests with browser windows visible, useful for seeing what's happening.

4. Debug mode

npm run test:e2e:debug

Runs tests with Playwright Inspector for step-by-step debugging.

Test Files

The repository includes 8 test suites in the tests/ directory:

  • calendar.spec.ts
  • chat.spec.ts
  • header.spec.ts
  • images.spec.ts
  • map.spec.ts
  • mobile.spec.ts
  • responsive.spec.ts
  • sidebar.spec.ts

Test Reports

After running tests, an HTML report is generated. If tests fail, the report should open automatically. You can also view it manually:

npx playwright show-report

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

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

The biggest blocker is that fullResponse += ... in manusTool likely does not update the caller’s context, so the agent won’t retain Manus task details for follow-up (correctness issue). The schema module exports a misleading PartialManus type based on the Zod schema object rather than its inferred shape. The tool registry’s any tool map increases drift risk for tool names/contracts. Finally, the Manus API call lacks a timeout and may surface overly verbose error bodies to downstream consumers.

Additional notes (1)
  • Maintainability | lib/agents/tools/index.tsx:9-9
    The tool registry uses const tools: any = { ... }, which defeats the benefit of registering tools conditionally while keeping tool names consistent and discoverable. In particular, manusTask becomes an untyped string key and increases the risk of mismatches between the tool name the agent is instructed to call and the actual registered name.

Even if the surrounding codebase is currently permissive, this is a maintainability footgun—new tools will keep piling into an any bag with no central contract.

Summary of changes

Summary of changes

✅ Added Manus AI integration

  • Introduced a new tool manusTool that creates tasks via POST https://api.manus.ai/v1/tasks and streams UI updates.
  • Added a Zod schema (lib/schema/manus.tsx) describing tool parameters (prompt, agentProfile, taskMode, interactiveMode).
  • Added a UI component (components/manus-section.tsx) to render the Manus task metadata and external links.

🔧 Tool registry & configuration updates

  • Registered the Manus tool conditionally in lib/agents/tools/index.tsx when process.env.MANUS_API_KEY is present.
  • Documented MANUS_API_KEY in .env.local.example.

📚 Documentation

  • Added MANUS_INTEGRATION.md with architecture rationale, setup, usage, and troubleshooting.

Comment on lines 7 to 106
export const manusTool = ({ uiStream, fullResponse }: ToolProps) => ({
description:
'Execute complex tasks using the Manus AI agent platform. Use this for multi-step tasks requiring planning, research across multiple sources, code execution, file manipulation, or advanced reasoning. Manus can browse the web, analyze data, write code, and perform complex workflows.',
parameters: manusSchema,
execute: async ({
prompt,
agentProfile,
taskMode,
interactiveMode
}: {
prompt: string
agentProfile: 'manus-1.6' | 'manus-1.6-lite' | 'manus-1.6-max'
taskMode?: 'chat' | 'adaptive' | 'agent'
interactiveMode: boolean
}) => {
let hasError = false
// Show loading state
uiStream.append(<SearchSkeleton />)

let taskResult: {
task_id: string
task_title: string
task_url: string
share_url?: string
} | null = null

try {
const apiKey = process.env.MANUS_API_KEY
if (!apiKey) {
throw new Error('MANUS_API_KEY is not configured')
}

// Create task
const response = await fetch('https://api.manus.ai/v1/tasks', {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
API_KEY: apiKey
},
body: JSON.stringify({
prompt,
agentProfile,
taskMode,
interactiveMode,
createShareableLink: true
})
})

if (!response.ok) {
const errorText = await response.text()
throw new Error(
`Manus API error: ${response.status} - ${errorText}`
)
}

taskResult = await response.json()

if (!taskResult) {
hasError = true
}
} catch (error) {
hasError = true
console.error('Manus API error:', error)

const errorMessage =
error instanceof Error ? error.message : 'Unknown error occurred'
fullResponse += `\nAn error occurred while executing Manus task: ${errorMessage}`

uiStream.update(
<Card className="p-4 mt-2 text-sm">
{`An error occurred while executing Manus task: ${errorMessage}`}
</Card>
)
return null
}

if (hasError || !taskResult) {
fullResponse += `\nAn error occurred while executing the Manus task.`
uiStream.update(
<Card className="p-4 mt-2 text-sm">
An error occurred while executing the Manus task.
</Card>
)
return null
}

// Update UI with task result
uiStream.update(<ManusSection data={taskResult} />)

// Add task information to the response context
fullResponse += `\n\nManus task created successfully:
- Task ID: ${taskResult.task_id}
- Title: ${taskResult.task_title}
- Task URL: ${taskResult.task_url}
${taskResult.share_url ? `- Share URL: ${taskResult.share_url}` : ''}

You can view the task progress and results at the provided URL.`

return taskResult

Choose a reason for hiding this comment

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

fullResponse is treated like a mutable accumulator (fullResponse += ...) but it’s passed in as a string prop. This won’t mutate the caller’s value (strings are immutable and this is just rebinding a local variable), so the Manus task details/errors likely never make it back into the agent’s context. This is a correctness issue: the tool may appear to work in UI, but the agent won’t “remember” the task URL/ID for follow-up reasoning.

You already stream UI via uiStream; for textual context you likely need either (a) to return a string payload the agent can incorporate, or (b) accept a mutable/streamable reference (e.g., fullResponseStream.append(...)) rather than a plain string.

Suggestion

Change the contract so the tool can actually add to the model-visible context.

Option A (recommended): remove fullResponse mutation and return a structured result with a summary string that the caller appends.

// tools/manus.tsx
export const manusTool = ({ uiStream }: ToolProps) => ({
  // ...
  execute: async (args) => {
    // ...
    return {
      ...taskResult,
      summary: `Manus task created successfully:\n- Task ID: ${taskResult.task_id}\n- Title: ${taskResult.task_title}\n- Task URL: ${taskResult.task_url}`
    }
  }
})

Then in the agent/tool runner layer, append result.summary into the conversation context.

Option B: change ToolProps.fullResponse to be a mutable container (e.g., { value: string }) or a streamable text primitive and append to it.

Reply with "@CharlieHelps yes please" if you’d like me to add a commit implementing Option A end-to-end (including updating the tool runner/registry call sites).

Comment on lines 40 to 61
const response = await fetch('https://api.manus.ai/v1/tasks', {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
API_KEY: apiKey
},
body: JSON.stringify({
prompt,
agentProfile,
taskMode,
interactiveMode,
createShareableLink: true
})
})

if (!response.ok) {
const errorText = await response.text()
throw new Error(
`Manus API error: ${response.status} - ${errorText}`
)
}

Choose a reason for hiding this comment

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

The API error handling includes await response.text() and embeds it directly in the thrown error message. Many APIs return verbose bodies that may include request correlation IDs, internal details, or even echoed inputs. This becomes risky if fullResponse (or any other output) is later shown to users or logged upstream.

Also, there is no timeout/abort for fetch, so a hung Manus request can stall the tool execution indefinitely.

Suggestion
  1. Add a timeout via AbortController.
  2. Sanitize error propagation: log full body server-side, but surface only status + a capped snippet (or a generic message) to the agent/user.

Example:

const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 30_000)

let errorBody: string | undefined
try {
  const response = await fetch(url, { ...opts, signal: controller.signal })
  if (!response.ok) {
    errorBody = await response.text()
    throw new Error(`Manus API error: ${response.status}`)
  }
  return await response.json()
} finally {
  clearTimeout(timeout)
  if (errorBody) console.error('Manus API error body:', errorBody)
}

Reply with "@CharlieHelps yes please" if you’d like me to add a commit implementing the timeout + safer error surfacing.

Comment on lines 39 to 53
// Create task
const response = await fetch('https://api.manus.ai/v1/tasks', {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
API_KEY: apiKey
},
body: JSON.stringify({
prompt,
agentProfile,
taskMode,
interactiveMode,
createShareableLink: true
})

Choose a reason for hiding this comment

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

The request headers use API_KEY, but many APIs expect an Authorization: Bearer ... header (or a vendor-specific header like X-API-Key). Since this integration hardcodes API_KEY, if Manus expects a different header name, the tool will fail at runtime.

Given this is an external integration, it’s worth aligning the header with Manus docs and/or supporting both to reduce fragility.

Suggestion

Confirm the required auth header from Manus docs and update accordingly. If docs are ambiguous or subject to change, consider sending both headers:

headers: {
  accept: 'application/json',
  'content-type': 'application/json',
  'X-API-Key': apiKey,
  Authorization: `Bearer ${apiKey}`
}

(Use only what Manus supports; don’t ship both unless confirmed acceptable.)

Reply with "@CharlieHelps yes please" if you’d like me to add a commit once you confirm the expected header.

Comment on lines 11 to 21
execute: async ({
prompt,
agentProfile,
taskMode,
interactiveMode
}: {
prompt: string
agentProfile: 'manus-1.6' | 'manus-1.6-lite' | 'manus-1.6-max'
taskMode?: 'chat' | 'adaptive' | 'agent'
interactiveMode: boolean
}) => {

Choose a reason for hiding this comment

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

The tool declares its execute args as a custom inline type that diverges from the Zod schema defaults:

  • agentProfile is required in the inline type, but schema provides a default.
  • interactiveMode is required in the inline type, but schema provides a default.

Even if the runtime tool framework fills defaults from Zod, keeping the execute signature stricter than the schema increases the chance of call-site mismatch and makes maintenance harder.

Suggestion

Derive the execute parameter type directly from the schema to keep schema + implementation aligned:

import { z } from 'zod'

execute: async (params: z.infer<typeof manusSchema>) => {
  const { prompt, agentProfile, taskMode, interactiveMode } = params
  ...
}

If you want to keep an explicit type, make fields with schema defaults optional in the type.

Reply with "@CharlieHelps yes please" if you’d like me to add a commit with this refactor.

@charliecreates charliecreates bot removed the request for review from CharlieHelps December 30, 2025 16:14
- Fix PartialManus type to use z.infer correctly
- Add fetch timeout with AbortController (30s)
- Sanitize error messages to prevent info leakage
- Validate URLs before rendering to prevent injection
- Filter undefined fields from request body
- Validate API response structure with Zod
- Add aria-labels for accessibility
- Disable shareable links by default for privacy
- Add audit logging for task creation
- Update documentation with security features

Addresses all review comments from CodeRabbit and qodo-code-review.
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: 1

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93b5d41 and 10fd451.

📒 Files selected for processing (4)
  • MANUS_INTEGRATION.md
  • components/manus-section.tsx
  • lib/agents/tools/manus.tsx
  • lib/schema/manus.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
components/manus-section.tsx (2)
components/section.tsx (1)
  • Section (24-75)
components/ui/card.tsx (3)
  • props (8-17)
  • props (35-44)
  • props (50-56)
lib/agents/tools/manus.tsx (3)
lib/agents/tools/index.tsx (2)
  • ToolProps (8-12)
  • ToolProps (14-39)
lib/schema/manus.tsx (2)
  • manusSchema (3-23)
  • ManusInput (25-25)
components/search-skeleton.tsx (1)
  • SearchSkeleton (6-30)
🪛 LanguageTool
MANUS_INTEGRATION.md

[style] ~107-~107: Consider a different adjective to strengthen your wording.
Context: ...uire: - Complex multi-step workflows - Deep research across multiple sources - Code...

(DEEP_PROFOUND)


[grammar] ~235-~235: Ensure spelling is correct
Context: ...tivity ### Timeout Errors If requests timeout: - Check your network connection - Verify t...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
MANUS_INTEGRATION.md

54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


59-59: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


64-64: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


68-68: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


73-73: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


78-78: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (4)
components/manus-section.tsx (1)

1-70: LGTM! Well-implemented component with strong accessibility and security practices.

The ManusSection component is well-structured and follows best practices:

  • Proper accessibility with descriptive aria-label attributes and aria-hidden on decorative icons
  • Secure external links with rel="noopener noreferrer"
  • Clean conditional rendering for optional share URL
  • Consistent with QCX design patterns (Section, Card components)
  • URL validation is handled upstream in the tool (lib/agents/tools/manus.tsx lines 110-117)
lib/schema/manus.tsx (1)

1-26: LGTM! Schema is well-defined with proper type inference.

The Zod schema is correctly structured with:

  • Clear field descriptions for each parameter
  • Appropriate defaults (manus-1.6 for agentProfile, false for interactiveMode)
  • Enum constraints for agentProfile and taskMode
  • Proper type inference using z.infer for ManusInput (line 25)
  • Correct Partial<ManusInput> for PartialManus (line 26)

The previous concern about type inference has been addressed.

lib/agents/tools/manus.tsx (2)

17-45: LGTM! Excellent security-focused helper functions.

The helper functions implement strong security measures:

sanitizeErrorMessage (lines 17-30):

  • Masks sensitive configuration errors (MANUS_API_KEY)
  • Replaces network errors with user-friendly messages
  • Prevents information leakage through error messages

isValidUrl (lines 33-45):

  • Enforces HTTPS protocol requirement
  • Restricts to manus.ai domain and subdomains
  • Prevents URL injection attacks

These align well with security best practices and address past review concerns.


47-156: LGTM! Comprehensive and secure tool implementation.

The Manus tool implementation demonstrates excellent attention to security and reliability:

Security measures (addressing past review concerns):

  • Request timeout: 30-second AbortController prevents indefinite hangs (lines 80-82, 97, 130-134)
  • Error sanitization: prevents sensitive data leakage (lines 140-145, using sanitizeErrorMessage)
  • URL validation: enforces HTTPS and manus.ai domain before rendering (lines 110-117)
  • Response validation: Zod schema ensures type safety (line 107)
  • Privacy by default: shareable links disabled (line 78)

Reliability features:

  • Filters undefined fields from requests (lines 62-74)
  • Comprehensive error handling with proper logging (lines 136-154)
  • Audit trail for task creation (lines 123-126)
  • Graceful error UI updates (lines 147-151)

Note: The fullResponse parameter exists but is unused. This is consistent with the broader tool architecture pattern across the codebase and is not an issue specific to this implementation.

Comment on lines +1 to +256
# Manus AI Integration

This document describes the integration of Manus AI into the QCX platform.

## Overview

Manus AI has been integrated as a specialized tool available to the QCX Researcher Agent. This integration enables QCX to delegate complex, multi-step tasks to the Manus AI platform, which can perform advanced reasoning, research, code execution, and workflow automation.

## Architecture Decision

The Manus integration follows a **tool-based architecture** rather than creating a separate agent. This decision was made based on several factors:

- **Consistency**: Aligns with existing QCX patterns (search, retrieve, geospatial tools)
- **Simplicity**: Minimal changes to existing routing logic
- **Flexibility**: The Researcher Agent can intelligently decide when Manus is appropriate
- **Maintainability**: Follows established patterns in the codebase
- **Cost Efficiency**: Avoids unnecessary LLM calls for routing decisions

## Implementation Details

### Files Created

1. **`lib/schema/manus.tsx`** - Zod schema for Manus tool parameters
- Defines validation for prompt, agentProfile, taskMode, and interactiveMode
- Provides TypeScript types for type safety using `z.infer`

2. **`lib/agents/tools/manus.tsx`** - Manus tool implementation
- Handles API calls to Manus AI platform
- Implements comprehensive error handling with sanitized messages
- Validates API responses and URLs for security
- Includes fetch timeout to prevent hanging requests
- Filters undefined fields from request body
- Manages UI updates with proper streaming

3. **`components/manus-section.tsx`** - UI component for displaying Manus task results
- Shows task title, ID, and status
- Provides accessible links with aria-labels
- Validates URLs before rendering
- Follows QCX design patterns

### Files Modified

1. **`lib/agents/tools/index.tsx`** - Tool registry
- Added Manus tool import
- Conditionally registers Manus tool when MANUS_API_KEY is present

2. **`.env.local.example`** - Environment configuration
- Added MANUS_API_KEY configuration with documentation

## Security Features

The integration implements several security measures:

### 1. Error Message Sanitization
- Raw API errors are not exposed to users
- Error messages are sanitized to prevent information leakage
- Sensitive configuration errors are replaced with generic messages

### 2. URL Validation
- All URLs from the API are validated before use
- Only HTTPS URLs from manus.ai domain are allowed
- Invalid URLs are rejected to prevent injection attacks

### 3. Request Timeout
- 30-second timeout on API requests using AbortController
- Prevents indefinite hanging on slow or unresponsive API

### 4. Response Validation
- API responses are validated against expected schema
- Malformed responses are rejected
- Type safety enforced with Zod validation

### 5. Audit Logging
- Task creation events are logged (without sensitive data)
- Errors are logged securely with timestamps
- Helps with debugging and compliance

### 6. Privacy by Default
- Shareable links are disabled by default
- Can be enabled when needed for specific use cases
- Prevents accidental data exposure

## Configuration

### Environment Variables

Add the following to your `.env.local` file:

```bash
# Manus AI API Key
# Get your API key from https://open.manus.im/docs/quickstart
MANUS_API_KEY="your_manus_api_key_here"
```

### Getting Your API Key

1. Visit the [Manus API Integration settings](https://open.manus.im/docs/quickstart)
2. Generate a new API key
3. Add it to your `.env.local` file
4. Restart your development server

## Usage

Once configured, the Manus tool is automatically available to the QCX Researcher Agent. The agent will use Manus for tasks that require:

- Complex multi-step workflows
- Deep research across multiple sources
- Code execution or file manipulation
- Advanced reasoning and planning
- Tasks beyond simple search and retrieval

### Tool Parameters

The Manus tool accepts the following parameters:

- **prompt** (required): The task instruction for Manus
- **agentProfile** (optional): Agent profile to use
- `manus-1.6` (default) - Balanced performance
- `manus-1.6-lite` - Faster execution
- `manus-1.6-max` - Most capable
- **taskMode** (optional): Execution mode
- `chat` - Conversational mode
- `adaptive` - Adaptive mode
- `agent` - Full agent mode
- **interactiveMode** (optional): Enable follow-up questions (default: false)

### Example Usage

When a user asks a complex question like "Research the latest developments in quantum computing and create a comprehensive report with code examples," the Researcher Agent may decide to use the Manus tool:

```typescript
{
prompt: "Research the latest developments in quantum computing and create a comprehensive report with code examples",
agentProfile: "manus-1.6",
taskMode: "agent",
interactiveMode: false
}
```

The tool will:
1. Validate the API key is configured
2. Filter undefined fields from the request
3. Create a task on the Manus platform with timeout protection
4. Validate the API response
5. Validate URLs before displaying
6. Show the task information with accessible links
7. Log the task creation for audit trail

## API Reference

### Manus API Endpoint

- **URL**: `https://api.manus.ai/v1/tasks`
- **Method**: POST
- **Authentication**: API_KEY header
- **Timeout**: 30 seconds

### Request Schema

```typescript
{
prompt: string
agentProfile: 'manus-1.6' | 'manus-1.6-lite' | 'manus-1.6-max'
taskMode?: 'chat' | 'adaptive' | 'agent'
interactiveMode?: boolean
createShareableLink?: boolean
}
```

### Response Schema

```typescript
{
task_id: string
task_title: string
task_url: string (validated HTTPS URL)
share_url?: string (validated HTTPS URL)
}
```

## Accessibility

The integration follows WCAG guidelines:

- Links have descriptive aria-labels
- Icons are marked as decorative with aria-hidden
- Semantic HTML structure
- Keyboard navigation support
- Screen reader friendly

## Future Enhancements

Potential improvements for future iterations:

1. **Task Result Polling**: Implement polling to fetch and display task results directly in QCX
2. **Webhook Integration**: Use Manus webhooks for real-time task completion notifications
3. **Connector Support**: Enable Manus connectors (Gmail, Notion, Google Calendar, etc.)
4. **Dedicated Agent**: Create a specialized Manus agent for complex workflows requiring multiple Manus calls
5. **File Attachments**: Support file uploads to Manus tasks
6. **Multi-turn Conversations**: Enable continuing existing Manus tasks
7. **Configurable Share Links**: Add UI option to enable/disable shareable links per task

## Testing

To test the integration:

1. Ensure MANUS_API_KEY is set in `.env.local`
2. Start the development server: `bun dev`
3. Ask a complex question that would benefit from Manus capabilities
4. Verify the Manus tool is called and task information is displayed
5. Check that URLs are valid and accessible
6. Verify error handling with invalid API key
7. Test timeout behavior with network issues

## Troubleshooting

### Tool Not Available

If the Manus tool is not being used:
- Check that MANUS_API_KEY is set in `.env.local`
- Restart the development server after adding the API key
- Verify the API key is valid by testing it directly with the Manus API

### API Errors

If you encounter API errors:
- Check the console for detailed error logs (sanitized for security)
- Verify your API key has not expired
- Ensure you have sufficient credits on your Manus account
- Check the Manus API status page for service issues
- Verify network connectivity

### Timeout Errors

If requests timeout:
- Check your network connection
- Verify the Manus API is responding
- Consider increasing the timeout if needed for complex tasks
- Check if firewall or proxy is blocking requests

### Invalid URLs

If URL validation fails:
- Verify the Manus API is returning valid HTTPS URLs
- Check that URLs are from manus.ai domain
- Report any issues to Manus support

## Resources

- [Manus API Documentation](https://open.manus.im/docs/quickstart)
- [Manus API Reference](https://open.manus.im/docs/api-reference)
- [AI SDK Workflow Patterns](https://ai-sdk.dev/docs/agents/workflows)
- [QCX Repository](https://github.com/QueueLab/QCX)
- [WCAG Accessibility Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)

## License
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

LGTM! Comprehensive and professional documentation.

The documentation is thorough and well-organized, covering:

  • Architecture rationale and decision-making
  • Complete implementation details and file structure
  • Security features (sanitization, validation, timeout, audit logging, privacy)
  • Clear configuration instructions with links to Manus documentation
  • Usage examples and parameter descriptions
  • API reference with request/response schemas
  • Accessibility guidelines
  • Troubleshooting section with common issues
  • Future enhancement ideas
Optional: Fix markdown heading spacing for linting

Static analysis suggests adding blank lines around some headings to comply with markdown linting rules. While this doesn't affect readability, it would resolve linting warnings:

Add blank lines before headings at lines 54, 59, 64, 68, 73, and 78 (the numbered security feature subsections).

Example:

 - Sensitive configuration errors are replaced with generic messages
+
 ### 2. URL Validation
 - All URLs from the API are validated before use

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 LanguageTool

[style] ~107-~107: Consider a different adjective to strengthen your wording.
Context: ...uire: - Complex multi-step workflows - Deep research across multiple sources - Code...

(DEEP_PROFOUND)


[grammar] ~235-~235: Ensure spelling is correct
Context: ...tivity ### Timeout Errors If requests timeout: - Check your network connection - Verify t...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)

54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


59-59: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


64-64: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


68-68: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


73-73: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


78-78: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
In MANUS_INTEGRATION.md lines 1 to 256, markdown linting warns about missing
blank lines around several subsection headings; add a single blank line before
the headings at (approx.) lines 54, 59, 64, 68, 73, and 78 so each numbered
security feature subsection is separated by an empty line from the preceding
content, ensuring the file conforms to standard markdown heading spacing rules.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants