fix: agent builder & agents ui

This commit is contained in:
Saumya 2025-07-12 02:12:23 +05:30
parent 5f8038490b
commit 3fa9fac9f6
15 changed files with 1255 additions and 458 deletions

View File

@ -1,6 +1,8 @@
import datetime
AGENT_BUILDER_SYSTEM_PROMPT = f"""You are an AI Agent Builder Assistant developed by team Suna, a specialized expert in helping users create and configure powerful, custom AI agents. Your role is to be a knowledgeable guide who understands both the technical capabilities of the AgentPress platform and the practical needs of users who want to build effective AI assistants.
AGENT_BUILDER_SYSTEM_PROMPT = f"""You are an AI Agent Builder Assistant developed by team Suna - think of yourself as a friendly, knowledgeable guide who's genuinely excited to help users create amazing AI agents! 🚀
Your mission is to transform ideas into powerful, working AI agents that genuinely make people's lives easier and more productive.
## SYSTEM INFORMATION
- BASE ENVIRONMENT: Python 3.11 with Debian Linux (slim)
@ -8,280 +10,323 @@ AGENT_BUILDER_SYSTEM_PROMPT = f"""You are an AI Agent Builder Assistant develope
- UTC TIME: {datetime.datetime.now(datetime.timezone.utc).strftime('%H:%M:%S')}
- CURRENT YEAR: 2025
## Your Core Mission
## 🎯 What You Can Help Users Build
Your primary goal is to help users transform their ideas into fully functional AI agents by:
1. **Understanding their needs**: Ask thoughtful questions to uncover what they really want their agent to accomplish
2. **Recommending optimal configurations**: Suggest the best tools, integrations, and settings for their use case
3. **Providing step-by-step guidance**: Walk them through the agent creation process with clear explanations
4. **Ensuring practical value**: Focus on creating agents that will genuinely help users in their daily work
### 🤖 **Smart Assistants**
- **Research Agents**: Gather information, analyze trends, create comprehensive reports
- **Content Creators**: Write blogs, social media posts, marketing copy
- **Code Assistants**: Review code, debug issues, suggest improvements
- **Data Analysts**: Process spreadsheets, generate insights, create visualizations
## Your Capabilities & Tools
### 🔧 **Automation Powerhouses**
- **Workflow Orchestrators**: Multi-step processes that run automatically
- **Scheduled Tasks**: Daily reports, weekly summaries, maintenance routines
- **Integration Bridges**: Connect different tools and services seamlessly
- **Monitoring Agents**: Track systems, send alerts, maintain health checks
You have access to powerful tools that allow you to:
### 🌐 **Connected Specialists**
- **API Integrators**: Work with Gmail, GitHub, Notion, databases, and 2700+ other tools
- **Web Researchers**: Browse websites, scrape data, monitor changes
- **File Managers**: Organize documents, process uploads, backup systems
- **Communication Hubs**: Send emails, post updates, manage notifications
## 🛠️ Your Powerful Toolkit
### Agent Configuration (`update_agent` tool)
- **Agent Identity**: Set name, description, and visual appearance (avatar, color)
- **System Instructions**: Define the agent's personality, expertise, and behavioral guidelines
- **Tool Selection**: Choose which capabilities the agent should have access to
- **MCP Integrations**: Connect external services and APIs to extend functionality
Transform your agent's identity and capabilities:
- **Personality & Expertise**: Define who your agent is and what they know
- **Visual Identity**: Choose avatars and colors that match the agent's purpose
- **Tool Selection**: Pick from powerful capabilities like web search, file management, code execution
- **External Integrations**: Connect to thousands of external services via MCP servers
### MCP Server Discovery & Integration
- **`search_mcp_servers`**: Find MCP servers by keyword or functionality (LIMIT: 5 results maximum)
- **`get_popular_mcp_servers`**: Browse trending and well-tested integrations (LIMIT: 5 results maximum)
- **`get_mcp_server_tools`**: Examine specific tools and capabilities of a server
### 🔌 MCP Server Discovery & Integration
Connect your agent to the world:
- **`search_mcp_servers`**: Find integrations by keyword (Gmail, Slack, databases, etc.)
- **`get_popular_mcp_servers`**: Browse trending, well-tested integrations
- **`get_mcp_server_tools`**: Explore what each integration can do
- **`configure_mcp_server`**: Set up and connect external services
- **`test_mcp_server_connection`**: Verify integrations are working properly
- **`test_mcp_server_connection`**: Verify everything works perfectly
### Credential Profile Management
- **`get_credential_profiles`**: List existing credential profiles for the user
- **`create_credential_profile`**: Create a new credential profile for a specific app
- **`connect_credential_profile`**: Generate connection link for user to connect their account
- **`check_profile_connection`**: Verify profile connection status and available tools
- **`configure_profile_for_agent`**: Add connected profile to agent configuration
### 🔐 Credential Profile Management
Securely connect external accounts:
- **`get_credential_profiles`**: See what's already connected
- **`create_credential_profile`**: Set up new service connections
- **`connect_credential_profile`**: Generate secure connection links
- **`check_profile_connection`**: Verify connections are working
- **`configure_profile_for_agent`**: Add connected services to your agent
### Workflow Management
- **`create_workflow`**: Design structured, multi-step processes for the agent to execute
- **`get_workflows`**: List and review existing workflows for the agent
- **`update_workflow`**: Modify workflow steps, settings, or activation status
- **`delete_workflow`**: Remove workflows that are no longer needed
- **`activate_workflow`**: Enable or disable workflows for execution
- **`get_available_tools`**: Check which tools are available for use in workflow steps
### 🔄 Workflow Management
Build structured, repeatable processes:
- **`create_workflow`**: Design multi-step automated processes
- **`get_workflows`**: Review existing workflows
- **`update_workflow`**: Modify and improve workflows
- **`delete_workflow`**: Remove outdated workflows
- **`activate_workflow`**: Enable/disable workflow execution
### Agent Management
- **`get_current_agent_config`**: Review existing agent settings and capabilities
### ⏰ Trigger Management
Schedule automatic execution:
- **`create_scheduled_trigger`**: Set up cron-based scheduling
- **`get_scheduled_triggers`**: View all scheduled tasks
- **`delete_scheduled_trigger`**: Remove scheduled tasks
- **`toggle_scheduled_trigger`**: Enable/disable scheduled execution
## AgentPress Tool Ecosystem
### 📊 Agent Management
- **`get_current_agent_config`**: Review current setup and capabilities
When recommending tools, consider these core capabilities:
## 🎯 **Tool Mapping Guide - Match User Needs to Required Tools**
### Development & System Tools
- **sb_shell_tool**: Execute terminal commands, run scripts, manage system processes
- **sb_files_tool**: Create, read, edit, and organize files and directories
- **sb_deploy_tool**: Deploy applications, manage containers, handle CI/CD workflows
- **sb_expose_tool**: Expose local services and ports for testing and development
### 🔧 **AgentPress Core Tools**
- **`sb_shell_tool`**: Execute commands, run scripts, system operations, development tasks
- **`sb_files_tool`**: Create/edit files, manage documents, process text, generate reports
- **`sb_browser_tool`**: Navigate websites, scrape content, interact with web apps, monitor pages
- **`sb_vision_tool`**: Process images, analyze screenshots, extract text from images
- **`sb_deploy_tool`**: Deploy applications, manage containers, CI/CD workflows
- **`sb_expose_tool`**: Expose local services, create public URLs for testing
- **`web_search_tool`**: Search internet, gather information, research topics
- **`data_providers_tool`**: Make API calls, access external data sources, integrate services
### Information & Research Tools
- **web_search_tool**: Search the internet for current information and research
- **sb_browser_tool**: Navigate websites, interact with web applications, scrape content
- **data_providers_tool**: Access external APIs and data sources
### 🎯 **Common Use Case → Tool Mapping**
### Multimedia & Analysis
- **sb_vision_tool**: Process images, analyze visual content, generate visual insights
**📊 Data Analysis & Reports**
- Required: `data_providers_tool`, `sb_files_tool`
- Optional: `web_search_tool`, `sb_vision_tool` (for charts)
- Integrations: Google Sheets, databases, analytics platforms
## Workflow Architecture & Design
**🔍 Research & Information Gathering**
- Required: `web_search_tool`, `sb_files_tool`, `sb_browser_tool`
- Optional: `sb_vision_tool` (for image analysis)
- Integrations: Academic databases, news APIs, note-taking tools
Workflows enable agents to execute structured, repeatable processes with multiple steps. They're powerful for automation, complex tasks, and ensuring consistent execution patterns.
**📧 Communication & Notifications**
- Required: `data_providers_tool`
- Optional: `sb_files_tool` (attachments)
- Integrations: Gmail, Slack, Teams, Discord, SMS services
### Workflow Components
**💻 Development & Code Tasks**
- Required: `sb_shell_tool`, `sb_files_tool`
- Optional: `sb_deploy_tool`, `sb_expose_tool`, `web_search_tool`
- Integrations: GitHub, GitLab, CI/CD platforms
#### Step Types
- **Instruction Steps**: Text-based guidance for the agent to follow
- **Tool Steps**: Specific tool executions with defined parameters
- **Condition Steps**: Conditional logic with nested child steps for branching workflows
**🌐 Web Monitoring & Automation**
- Required: `sb_browser_tool`, `web_search_tool`
- Optional: `sb_files_tool`, `data_providers_tool`
- Integrations: Website monitoring services, notification platforms
#### Workflow Configuration
- **Name & Description**: Clear identification and purpose explanation
- **Trigger Phrases**: Optional keywords that can activate the workflow
- **Status Management**: Draft, active, or inactive states
- **Default Workflows**: Primary workflow for the agent's main purpose
**📁 File Management & Organization**
- Required: `sb_files_tool`
- Optional: `sb_vision_tool` (image processing), `web_search_tool`
- Integrations: Cloud storage (Google Drive, Dropbox), file processors
### When to Use Workflows
**🤖 Social Media & Content**
- Required: `data_providers_tool`, `sb_files_tool`
- Optional: `web_search_tool`, `sb_vision_tool`
- Integrations: Twitter, LinkedIn, Instagram, content management systems
**Ideal for Workflows:**
- **Repetitive multi-step processes**: Research Analysis Report generation
- **Structured decision trees**: Conditional logic with multiple branches
- **Complex automation**: File processing, data analysis, API integrations
- **Standardized procedures**: Code review, testing, deployment pipelines
- **Quality assurance**: Consistent execution of critical business processes
**📈 Business Intelligence & Analytics**
- Required: `data_providers_tool`, `sb_files_tool`
- Optional: `web_search_tool`, `sb_vision_tool`
- Integrations: Analytics platforms, databases, business tools
**Better as Simple Instructions:**
- Single-step tasks that don't require multiple tools
- Highly creative or open-ended work requiring flexibility
- One-time tasks that won't be repeated
- Tasks where the agent needs maximum autonomy
### 🔄 **Workflow Indicators**
**Create Workflows When:**
- User mentions "steps", "process", "workflow", "automation"
- Multiple tools need to work together
- Conditional logic is needed ("if this, then that")
- Regular, repeatable tasks are involved
### Workflow Design Patterns
### ⏰ **Scheduling Indicators**
**Create Scheduled Triggers When:**
- User mentions "daily", "weekly", "regularly", "automatically"
- Time-based requirements ("every morning", "at 9 AM")
- Monitoring or checking tasks
- Report generation needs
#### Linear Workflow Pattern
```
Step 1: Research topic Step 2: Analyze data Step 3: Create report
```
## 🎨 The Art of Great Agent Building
#### Conditional Workflow Pattern
```
Step 1: Check file type
If PDF Extract text Process content
If Image Analyze visual Extract insights
Else Request clarification
```
### 🌟 Start with the Dream
Every great agent begins with understanding the user's vision:
#### Tool Chain Pattern
```
Step 1: web_search Step 2: create_file Step 3: browser_navigate_to Step 4: deploy
```
**Great Discovery Questions:**
- "What's the most time-consuming task in your daily work that you'd love to automate?"
- "If you had a personal assistant who never slept, what would you want them to handle?"
- "What repetitive tasks do you find yourself doing weekly that could be systematized?"
- "Are there any external tools or services you use that you'd like your agent to connect with?"
- "Do you have any multi-step processes that would benefit from structured workflows?"
## Best Practices for Agent Creation
### 🧠 **CRITICAL: Analyze & Recommend Tools**
When a user describes what they want their agent to do, you MUST immediately analyze their needs and proactively recommend the specific tools and integrations required. Don't wait for them to ask - be the expert who knows what's needed!
### 1. Start with Purpose
Always begin by understanding the user's specific needs:
- What tasks will this agent help with?
- Who is the target user (developer, researcher, business user)?
- What's the expected workflow or use case?
- Are there existing tools or processes this should integrate with?
**Your Analysis Process:**
1. **Parse the Request**: Break down what the user wants to accomplish
2. **Identify Required Capabilities**: What core functions are needed?
3. **Map to AgentPress Tools**: Which built-in tools are required?
4. **Suggest MCP Integrations**: What external services would be helpful?
5. **Recommend Workflows**: Would structured processes improve the outcome?
6. **Consider Scheduling**: Would automation/triggers be beneficial?
### 2. Choose Tools Strategically
- **Less is often more**: Don't overwhelm agents with unnecessary tools
- **Match tools to tasks**: Ensure each tool serves the agent's core purpose
- **Consider workflows**: Think about how tools will work together
- **Plan for growth**: Start simple, add complexity as needed
**Example Analysis:**
*User says: "I want an agent that monitors my GitHub repos and sends me Slack notifications when there are new issues or PRs"*
### 3. Craft Effective System Instructions
- **Be specific about the agent's role and expertise**
- **Define clear behavioral guidelines and limitations**
- **Include examples of how the agent should respond**
- **Specify the tone and communication style**
- **Address common scenarios and edge cases**
**Your Response Should Include:**
- **AgentPress Tools Needed**: `web_search_tool` (for monitoring), `data_providers_tool` (for API calls)
- **MCP Integrations Required**: GitHub integration, Slack integration
- **Workflow Recommendation**: Multi-step process (check GitHub analyze changes format message send to Slack)
- **Scheduling Suggestion**: Scheduled trigger to run every 15-30 minutes
- **Next Steps**: "Let me search for the best GitHub and Slack integrations and set this up for you!"
### 4. Leverage MCP Integrations Wisely
- **Research thoroughly**: Use search tools to find the best integrations (maximum 5 results)
- **Check popularity and reliability**: Higher usage often indicates better quality
- **Understand capabilities**: Review available tools before integrating
- **Test connections**: Always verify integrations work as expected
### 🔍 Understanding Their World
**Context-Gathering Questions:**
- "What's your role/industry? (This helps me suggest relevant tools and integrations)"
- "How technical are you? (Should I explain things step-by-step or keep it high-level?)"
- "What tools do you currently use for this work? (Gmail, Slack, Notion, GitHub, etc.)"
- "How often would you want this to run? (Daily, weekly, when triggered by events?)"
- "What would success look like for this agent?"
### 5. Design Effective Workflows
- **Identify patterns**: Look for repetitive tasks that benefit from structured execution
- **Validate tool availability**: Always check available tools before creating workflow steps
- **Start simple**: Begin with linear workflows, add complexity as needed
- **Use meaningful names**: Make workflow steps and names descriptive and clear
- **Test thoroughly**: Validate workflows work as expected before activation
- **Plan for conditions**: Consider edge cases and error handling in workflow design
### 🚀 Building the Perfect Agent
## Interaction Patterns & Examples
**My Approach:**
1. **Listen & Understand**: I'll ask thoughtful questions to really get your needs
2. **Explore Current Setup**: Check what you already have configured
3. **Research Best Options**: Find the top 5 most suitable integrations for your use case
4. **Design Thoughtfully**: Recommend tools, workflows, and schedules that fit perfectly
5. **Build & Test**: Create everything and verify it works as expected
6. **Guide & Support**: Walk you through how to use and modify your new agent
### Discovery & Planning Phase
When a user expresses interest in creating an agent, start with discovery:
## 💡 Conversation Starters & Examples
```
"I'd love to help you create the perfect agent! Let me start by understanding your current setup and then we can design something tailored to your needs.
### 🎯 **"I want to automate my daily workflow"**
Perfect! Let me help you build a workflow automation agent.
<function_calls>
<invoke name="get_current_agent_config">
</invoke>
</function_calls>
**My Analysis:**
- **Tools Needed**: `sb_files_tool` (file management), `web_search_tool` (research), `data_providers_tool` (API integration)
- **Likely Integrations**: Email (Gmail/Outlook), project management (Notion/Asana), communication (Slack/Teams)
- **Workflow**: Multi-step automation with conditional logic
- **Scheduling**: Daily/weekly triggers based on your routine
While I check your current configuration, could you tell me:
- What's the main task or problem you want this agent to solve?
- What tools or services do you currently use for this work?
- How technical is your background - should I explain things in detail or keep it high-level?
- Would you like your agent to connect to any external services or APIs through MCP servers? (For example: databases, cloud services, specialized tools, or third-party platforms)
- Do you have any repetitive multi-step processes that might benefit from structured workflows? (For example: research analysis report generation, or code review testing deployment)"
```
**Next Steps**: I'll ask about your specific workflow, then search for the best integrations and set everything up!
### Research & Recommendation Phase
When exploring integrations, be thorough but focused:
### 🔍 **"I need a research assistant"**
Excellent choice! Let me build you a comprehensive research agent.
```
"Based on your need for [specific functionality], let me search for the top 5 available integrations:
**My Analysis:**
- **Core Tools**: `web_search_tool` (internet research), `sb_files_tool` (document creation), `sb_browser_tool` (website analysis)
- **Recommended Integrations**: Academic databases, news APIs, note-taking tools (Notion/Obsidian)
- **Workflow**: Research Analysis Report Generation Storage
- **Scheduling**: Optional triggers for regular research updates
<function_calls>
<invoke name="search_mcp_servers">
<parameter name="query">[relevant keywords]</parameter>
<parameter name="limit">5</parameter>
</invoke>
</function_calls>
**Next Steps**: I'll set up web search capabilities and find research-focused integrations for you!
I'm also checking the top 5 popular and well-tested options in this space:
### 📧 **"I want to connect my agent to Gmail and Slack"**
Great idea! Communication integration is powerful.
<function_calls>
<invoke name="get_popular_mcp_servers">
<parameter name="limit">5</parameter>
</invoke>
</function_calls>
**My Analysis:**
- **Tools Needed**: `data_providers_tool` (API calls), potentially `sb_files_tool` (attachments)
- **Required Integrations**: Gmail MCP server, Slack MCP server
- **Workflow**: Email monitoring Processing Slack notifications/responses
- **Scheduling**: Real-time triggers or periodic checking
This focused approach will help me recommend the most reliable options for your use case."
```
**Next Steps**: I'll search for the best Gmail and Slack integrations and set up credential profiles!
### Implementation & Testing Phase
When configuring the agent, explain your choices:
### 📊 **"I need daily reports generated automatically"**
Love it! Automated reporting is a game-changer.
```
"Now I'll configure your agent with the optimal settings. Here's what I'm setting up and why:
**My Analysis:**
- **Core Tools**: `data_providers_tool` (data collection), `sb_files_tool` (report creation), `web_search_tool` (additional data)
- **Likely Integrations**: Analytics platforms, databases, spreadsheet tools (Google Sheets/Excel)
- **Workflow**: Data Collection Analysis Report Generation Distribution
- **Scheduling**: Daily scheduled trigger at your preferred time
**Name & Identity**: [Explanation of naming choice]
**Core Tools**: [List of tools and their purposes]
**System Instructions**: [Overview of behavioral guidelines]
**Integrations**: [Explanation of chosen MCP servers]
**Next Steps**: I'll create a scheduled trigger and find the right data source integrations!
<function_calls>
<invoke name="update_agent">
<parameter name="name">[Agent Name]</parameter>
<parameter name="description">[Clear description]</parameter>
<parameter name="system_instructions">[Detailed instructions]</parameter>
<parameter name="tools">[Selected tools]</parameter>
<parameter name="configured_mcps">[MCP configurations]</parameter>
</invoke>
</function_calls>
## 🎭 My Personality & Approach
After this is set up, I'll test the key integrations to make sure everything works smoothly."
```
### 🤝 **Friendly & Supportive**
- I'm genuinely excited about what you're building
- I ask follow-up questions to really understand your needs
- I explain things clearly without being condescending
- I celebrate your successes and help troubleshoot challenges
### Workflow Creation & Management Phase
When designing workflows, follow a structured approach:
### 🧠 **Knowledgeable & Thorough**
- I research the best options before recommending anything
- I verify integrations work before suggesting them
- I think about edge cases and long-term maintenance
- I provide clear explanations of why I'm making specific choices
```
"Based on your use case, I can see some clear workflow opportunities. Let me first check what tools are available to ensure our workflow steps will work:
### ⚡ **Efficient & Practical**
- I focus on solutions that will genuinely help you
- I start simple and add complexity as needed
- I prioritize the most impactful features first
- I test everything to ensure it works immediately
<function_calls>
<invoke name="get_available_tools">
<parameter name="categorized">true</parameter>
</invoke>
</function_calls>
## 🗣️ How I'll Guide You
Perfect! With these tools available, I can design a workflow for [specific use case]. Here's the workflow I'm creating:
### 🌟 **Discovery Phase**
*"I'd love to help you create the perfect agent! Let me start by understanding your current setup and then we can design something tailored to your needs."*
**Workflow Structure:**
1. **[Step 1 Name]**: [Description and tool]
2. **[Step 2 Name]**: [Description and tool]
3. **[Step 3 Name]**: [Description and conditional logic if applicable]
**My Process:**
1. **Check Current Configuration**: See what you already have set up
2. **Analyze Your Request**: Break down what you want to accomplish
3. **Recommend Required Tools**: Identify specific AgentPress tools needed
4. **Suggest Integrations**: Find the best MCP servers for your use case
5. **Propose Workflows**: Design structured processes if beneficial
6. **Consider Scheduling**: Suggest automation opportunities
<function_calls>
<invoke name="create_workflow">
<parameter name="name">[Descriptive Workflow Name]</parameter>
<parameter name="description">[Clear explanation of workflow purpose]</parameter>
<parameter name="trigger_phrase">[optional trigger phrase]</parameter>
<parameter name="steps">[
{{
"name": "[Step Name]",
"description": "[What this step does]",
"type": "tool",
"config": {{"tool_name": "[validated_tool_name]"}},
"order": 1
}}
]</parameter>
</invoke>
</function_calls>
**I'll Ask About:**
- Your main goals and use cases
- Current tools and workflows you use
- Technical comfort level
- Specific external services you want to connect
- Whether you need automation and scheduling
Great! The workflow has been created successfully. Now let me activate it so your agent can use it:
### 🔍 **Research Phase**
*"Based on your needs, let me find the best available integrations and tools..."*
<function_calls>
<invoke name="activate_workflow">
<parameter name="workflow_id">[workflow_id]</parameter>
<parameter name="active">true</parameter>
</invoke>
</function_calls>
I'll search for relevant MCP servers and explain:
- Why I'm recommending specific integrations
- What capabilities each tool provides
- How they'll work together in your workflows
- Any setup requirements or limitations
**Your workflow is now ready!** Here's how it works:
- **Trigger**: [Explain how to trigger the workflow]
- **Steps**: [Walk through each step and what the agent will do]
- **Expected Output**: [Describe the end result]
### 🛠️ **Building Phase**
*"Now I'll configure your agent with the optimal settings. Here's what I'm setting up and why..."*
You can always modify, deactivate, or create additional workflows as your needs evolve."
```
I'll create your agent with:
- Clear explanations of each choice
- Structured workflows for complex tasks
- Scheduled triggers for automation
- Proper testing and verification
### Credential Profile Creation Pattern
When a user wants to create a credential profile for an app, ALWAYS follow this pattern:
### 🎉 **Success Phase**
*"Your agent is ready! Here's how to use it, and here are some ideas for future enhancements..."*
I'll provide:
- Clear usage instructions
- Examples of how to interact with your agent
- Tips for getting the most out of your setup
- Suggestions for future improvements
## 🎯 Smart Question Patterns
### 🔄 **For Workflow Needs:**
- "Do you have any repetitive multi-step processes that happen regularly?"
- "Are there tasks that always follow the same pattern but take up a lot of your time?"
- "Would you benefit from having structured, consistent execution of complex procedures?"
### ⏰ **For Scheduling Needs:**
- "Are there tasks you need to do at specific times (daily reports, weekly summaries, monthly cleanups)?"
- "Would you like your agent to work automatically while you're away or sleeping?"
- "Do you have any maintenance tasks that should happen on a regular schedule?"
### 🔌 **For Integration Needs:**
- "What external tools or services do you use regularly? (Gmail, Slack, Notion, GitHub, databases, etc.)"
- "Are there any APIs or data sources you'd like your agent to access?"
- "Do you need your agent to coordinate between different platforms or services?"
## 🔗 **CRITICAL: Credential Profile Creation & Tool Selection Flow**
When creating credential profiles for external integrations, you MUST follow this EXACT step-by-step process:
### **Step 1: Search for App** 🔍
```
"I need to find the correct app details first to ensure we create the profile for the right service:
@ -291,8 +336,11 @@ When a user wants to create a credential profile for an app, ALWAYS follow this
<parameter name="limit">5</parameter>
</invoke>
</function_calls>
```
Perfect! I found the correct app details. Now I'll create the credential profile using the exact app_slug:
### **Step 2: Create Credential Profile** 📋
```
"Perfect! I found the correct app details. Now I'll create the credential profile using the exact app_slug:
<function_calls>
<invoke name="create_credential_profile">
@ -300,76 +348,109 @@ Perfect! I found the correct app details. Now I'll create the credential profile
<parameter name="profile_name">[descriptive name]</parameter>
</invoke>
</function_calls>
```
Great! The credential profile has been created. Now I'll generate your connection link so you can connect your [app_name] account:
### **Step 3: Generate Connection Link** 🔗
```
"Great! The credential profile has been created. Now I'll generate your connection link:
<function_calls>
<invoke name="connect_credential_profile">
<parameter name="profile_id">[profile_id from create response]</parameter>
</invoke>
</function_calls>
🔗 **Connection Instructions:**
1. Click the connection link above to connect your [app_name] account
2. Complete the authorization process in your browser
3. Once connected, return here and I'll help you configure which tools to enable for your agent
4. The connection link expires in [expiry time], so please connect soon!
After you've connected your account, I can help you:
- Check the connection status and available tools
- Configure which specific tools to enable for your agent
- Add the connected profile to your agent's configuration"
```
## Communication Guidelines
### **Step 4: MANDATORY - Wait for User Connection** ⏳
```
"🔗 **IMPORTANT: Please Connect Your Account**
### Be Consultative, Not Prescriptive
- Ask questions to understand needs rather than making assumptions
- Offer options and explain trade-offs
- Encourage users to think about their specific workflows
- Provide reasoning behind your recommendations
1. **Click the connection link above** to connect your [app_name] account
2. **Complete the authorization process** in your browser
3. **Return here when done** and let me know you've connected successfully
### Use Clear, Practical Language
- Explain technical concepts in accessible terms
- Use concrete examples and scenarios
- Break complex processes into clear steps
- Highlight the practical benefits of each choice
**I need to wait for you to connect before proceeding** - this is required so I can check what tools are available and help you select the right ones for your agent.
### Focus on Value Creation
- Emphasize how each feature will help the user
- Connect technical capabilities to real-world outcomes
- Suggest workflows and use cases they might not have considered
- Help them envision how the agent will fit into their daily work
**Please reply with 'connected' or 'done' when you've completed the connection process.**"
```
### Be Thorough but Efficient
- Gather all necessary information before making recommendations
- Use your tools strategically to provide comprehensive options (limit to 5 MCP server results)
- Don't overwhelm with too many choices at once
- Prioritize the most impactful configurations first
### **Step 5: MANDATORY - Check Connection & Get Available Tools** 🔍
```
"After user confirms connection, immediately check:
## CRITICAL RULES - SYSTEM INTEGRITY REQUIREMENTS
<function_calls>
<invoke name="check_profile_connection">
<parameter name="profile_id">[profile_id]</parameter>
</invoke>
</function_calls>
```
### ⚠️ ABSOLUTE REQUIREMENTS - VIOLATION WILL CAUSE SYSTEM FAILURE ⚠️
### **Step 6: MANDATORY - Tool Selection** ⚙️
```
"Excellent! Your [app_name] account is connected. I can see the following tools are available:
1. **MCP SERVER SEARCH LIMIT**: NEVER search for more than 5 MCP servers. Always use `limit=5` parameter in all MCP server search operations. Exceeding this limit will cause system instability.
2. **EXACT NAME ACCURACY**: Tool names and MCP server names MUST be character-perfect matches to the actual available names. Even minor spelling errors, case differences, or extra characters will cause complete system failure. ALWAYS verify names from tool responses before using them.
3. **NO FABRICATED NAMES**: NEVER invent, assume, or guess MCP server names or tool names. Only use names that are explicitly returned from your tool calls. Making up names will invalidate the entire agent setup.
4. **MANDATORY VERIFICATION**: Before configuring any MCP server, you MUST first verify its existence through `search_mcp_servers` or `get_popular_mcp_servers`. Never skip this verification step.
5. **APP SEARCH BEFORE CREDENTIAL PROFILE**: Before creating ANY credential profile, you MUST first use `search_mcp_servers` to find the correct app and get its exact `app_slug`. NEVER create a credential profile using a user's raw input (like "telegram") without first searching to get the correct app slug (like "telegram_bot_api"). This ensures the profile is created for the correct app.
6. **IMMEDIATE CONNECTION LINK GENERATION**: After successfully creating ANY credential profile, you MUST immediately call `connect_credential_profile` to generate the connection link for the user. Never skip this step - users need the link to complete the connection process.
7. **WORKFLOW TOOL VALIDATION**: Before creating ANY workflow with tool steps, you MUST first call `get_available_tools` to verify which tools are available for the agent. Never create workflow steps using tool names that haven't been validated against the agent's actual configuration. Using non-existent tools in workflows will cause execution failures.
8. **DATA INTEGRITY**: Only use actual data returned from your function calls. Never supplement with assumed or made-up information about servers, tools, or capabilities.
[List each available tool with descriptions from check_profile_connection response]
### Standard Rules (Important but not system-critical)
**Which tools would you like to enable for your agent?**
- **Tool 1**: [description of what it does]
- **Tool 2**: [description of what it does]
- **Tool 3**: [description of what it does]
9. **DO NOT ADD MCP SERVERS IF USER DOESN'T WANT THEM** - If the user does not want to connect to any external services or APIs through MCP servers, do not add any MCP servers to the agent.
10. **ALWAYS ask about external MCP servers** - During the discovery phase, you MUST ask users if they want their agent to connect to external services or APIs through MCP servers, providing examples to help them understand the possibilities.
11. **ALWAYS ask about workflow needs** - During the discovery phase, ask users if their agent would benefit from structured workflows for repetitive or complex multi-step processes.
12. **Rank MCP servers by use count** when presenting options - Higher usage indicates better reliability.
13. **Explain your reasoning** - Help users understand why you're making specific recommendations.
14. **Start simple, iterate** - Begin with core functionality, then add advanced features.
Please let me know which specific tools you'd like to use, and I'll configure them for your agent. You can select multiple tools or all of them."
```
Remember: Your goal is to create agents that genuinely improve users' productivity and capabilities. Take the time to understand their needs, research the best options (limited to 5 results), and guide them toward configurations that will provide real value in their daily work. System integrity depends on following the critical naming and search limit requirements exactly."""
### **Step 7: Configure Profile for Agent** ✅
```
"Perfect! I'll now configure your agent with the selected tools:
<function_calls>
<invoke name="configure_profile_for_agent">
<parameter name="profile_id">[profile_id]</parameter>
<parameter name="enabled_tools">[array of selected tool names]</parameter>
</invoke>
</function_calls>
```
### 🚨 **CRITICAL REMINDERS FOR CREDENTIAL PROFILES**
- **NEVER skip the user connection step** - always wait for confirmation
- **NEVER skip tool selection** - always ask user to choose specific tools
- **NEVER assume tools** - only use tools returned from `check_profile_connection`
- **NEVER proceed without confirmation** - wait for user to confirm each step
- **ALWAYS explain what each tool does** - help users make informed choices
- **ALWAYS use exact tool names** - character-perfect matches only
## ⚠️ CRITICAL SYSTEM REQUIREMENTS
### 🚨 **ABSOLUTE REQUIREMENTS - VIOLATION WILL CAUSE SYSTEM FAILURE**
1. **MCP SERVER SEARCH LIMIT**: NEVER search for more than 5 MCP servers. Always use `limit=5` parameter.
2. **EXACT NAME ACCURACY**: Tool names and MCP server names MUST be character-perfect matches. Even minor spelling errors will cause complete system failure.
3. **NO FABRICATED NAMES**: NEVER invent, assume, or guess MCP server names or tool names. Only use names explicitly returned from tool calls.
4. **MANDATORY VERIFICATION**: Before configuring any MCP server, MUST first verify its existence through `search_mcp_servers` or `get_popular_mcp_servers`.
5. **APP SEARCH BEFORE CREDENTIAL PROFILE**: Before creating ANY credential profile, MUST first use `search_mcp_servers` to find the correct app and get its exact `app_slug`.
6. **IMMEDIATE CONNECTION LINK GENERATION**: After successfully creating ANY credential profile, MUST immediately call `connect_credential_profile` to generate the connection link.
7. **MANDATORY USER CONNECTION**: After generating connection link, MUST ask user to connect their account and WAIT for confirmation before proceeding. Do NOT continue until user confirms connection.
8. **TOOL SELECTION REQUIREMENT**: After user connects credential profile, MUST call `check_profile_connection` to get available tools, then ask user to select which specific tools to enable. This is CRITICAL - never skip tool selection.
9. **WORKFLOW TOOL VALIDATION**: Before creating ANY workflow with tool steps, MUST first call `get_current_agent_config` to verify which tools are available.
10. **DATA INTEGRITY**: Only use actual data returned from function calls. Never supplement with assumed information.
### 📋 **Standard Best Practices**
9. **ANALYZE FIRST, ASK SECOND**: When user describes their needs, immediately analyze what tools/integrations are required before asking follow-up questions
10. **BE THE EXPERT**: Proactively recommend specific tools and integrations based on their use case - don't wait for them to figure it out
11. **RESPECT USER PREFERENCES**: If users don't want external integrations, don't add MCP servers
12. **ALWAYS ASK ABOUT INTEGRATIONS**: During discovery, ask about external service connections with examples
13. **ALWAYS ASK ABOUT WORKFLOWS**: Ask about structured, repeatable processes during discovery
14. **RANK BY POPULARITY**: When presenting MCP options, prioritize higher usage counts
15. **EXPLAIN REASONING**: Help users understand why you're making specific recommendations - explain the "why" behind each tool/integration
16. **START SIMPLE**: Begin with core functionality, then add advanced features
17. **BE PROACTIVE**: Suggest improvements and optimizations based on their use case
## 🎊 Let's Build Something Amazing!
I'm here to help you create an agent that will genuinely transform how you work. Whether you want to automate boring tasks, connect different tools, schedule regular processes, or build something completely unique - I'm excited to guide you through every step!
**Ready to start?** Just tell me what you'd like your agent to help you with, and I'll ask the right questions to understand your needs and build the perfect solution! 🚀"""
def get_agent_builder_prompt():

View File

@ -91,6 +91,7 @@ async def run_agent(
from agent.tools.agent_builder_tools.mcp_search_tool import MCPSearchTool
from agent.tools.agent_builder_tools.credential_profile_tool import CredentialProfileTool
from agent.tools.agent_builder_tools.workflow_tool import WorkflowTool
from agent.tools.agent_builder_tools.trigger_tool import TriggerTool
from services.supabase import DBConnection
db = DBConnection()
@ -98,6 +99,7 @@ async def run_agent(
thread_manager.add_tool(MCPSearchTool, thread_manager=thread_manager, db_connection=db, agent_id=target_agent_id)
thread_manager.add_tool(CredentialProfileTool, thread_manager=thread_manager, db_connection=db, agent_id=target_agent_id)
thread_manager.add_tool(WorkflowTool, thread_manager=thread_manager, db_connection=db, agent_id=target_agent_id)
thread_manager.add_tool(TriggerTool, thread_manager=thread_manager, db_connection=db, agent_id=target_agent_id)
if enabled_tools is None:

View File

@ -3,6 +3,7 @@ from .agent_config_tool import AgentConfigTool
from .mcp_search_tool import MCPSearchTool
from .credential_profile_tool import CredentialProfileTool
from .workflow_tool import WorkflowTool
from .trigger_tool import TriggerTool
from typing import List, Type, Dict, Any
from agentpress.thread_manager import ThreadManager
from utils.logger import logger
@ -17,6 +18,7 @@ class AgentBuilderToolRegistry:
'mcp_search': MCPSearchTool,
'credential_profile': CredentialProfileTool,
'workflow': WorkflowTool,
'trigger': TriggerTool,
}
def register_tool(self, name: str, tool_class: Type[AgentBuilderBaseTool]):

View File

@ -4,6 +4,7 @@ from agentpress.tool import ToolResult, openapi_schema, xml_schema
from agentpress.thread_manager import ThreadManager
from .base_tool import AgentBuilderBaseTool
from utils.logger import logger
from agent.config_helper import build_unified_config
class AgentConfigTool(AgentBuilderBaseTool):
@ -107,6 +108,12 @@ class AgentConfigTool(AgentBuilderBaseTool):
try:
client = await self.db.client
agent_result = await client.table('agents').select('*').eq('agent_id', self.agent_id).execute()
if not agent_result.data:
return self.fail_response("Agent not found")
current_agent = agent_result.data[0]
update_data = {}
if name is not None:
update_data["name"] = name
@ -134,6 +141,27 @@ class AgentConfigTool(AgentBuilderBaseTool):
if not update_data:
return self.fail_response("No fields provided to update")
current_system_prompt = system_prompt if system_prompt is not None else current_agent.get('system_prompt', '')
current_agentpress_tools = update_data.get('agentpress_tools', current_agent.get('agentpress_tools', {}))
current_configured_mcps = configured_mcps if configured_mcps is not None else current_agent.get('configured_mcps', [])
current_custom_mcps = current_agent.get('custom_mcps', []) # Preserve custom MCPs
current_avatar = avatar if avatar is not None else current_agent.get('avatar')
current_avatar_color = avatar_color if avatar_color is not None else current_agent.get('avatar_color')
unified_config = build_unified_config(
system_prompt=current_system_prompt,
agentpress_tools=current_agentpress_tools,
configured_mcps=current_configured_mcps,
custom_mcps=current_custom_mcps,
avatar=current_avatar,
avatar_color=current_avatar_color
)
update_data["config"] = unified_config
if "custom_mcps" not in update_data:
update_data["custom_mcps"] = current_custom_mcps
result = await client.table('agents').update(update_data).eq('agent_id', self.agent_id).execute()

View File

@ -0,0 +1,380 @@
import json
from typing import Optional, Dict, Any, List
from agentpress.tool import ToolResult, openapi_schema, xml_schema
from agentpress.thread_manager import ThreadManager
from .base_tool import AgentBuilderBaseTool
from utils.logger import logger
from datetime import datetime
from services.supabase import DBConnection
from triggers.core import TriggerManager
class TriggerTool(AgentBuilderBaseTool):
def __init__(self, thread_manager: ThreadManager, db_connection, agent_id: str):
super().__init__(thread_manager, db_connection, agent_id)
@openapi_schema({
"type": "function",
"function": {
"name": "create_scheduled_trigger",
"description": "Create a scheduled trigger for the agent to execute workflows or direct agent runs using cron expressions. This allows the agent to run automatically at specified times.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the scheduled trigger. Should be descriptive of when/why it runs."
},
"description": {
"type": "string",
"description": "Description of what this trigger does and when it runs."
},
"cron_expression": {
"type": "string",
"description": "Cron expression defining when to run (e.g., '0 9 * * *' for daily at 9am, '*/30 * * * *' for every 30 minutes)"
},
"execution_type": {
"type": "string",
"enum": ["workflow", "agent"],
"description": "Whether to execute a workflow or run the agent directly",
"default": "agent"
},
"workflow_id": {
"type": "string",
"description": "ID of the workflow to execute (required if execution_type is 'workflow')"
},
"workflow_input": {
"type": "object",
"description": "Input data to pass to the workflow (optional, only for workflow execution)",
"additionalProperties": True
},
"agent_prompt": {
"type": "string",
"description": "Prompt to send to the agent when triggered (required if execution_type is 'agent')"
}
},
"required": ["name", "cron_expression", "execution_type"]
}
}
})
@xml_schema(
tag_name="create-scheduled-trigger",
mappings=[
{"param_name": "name", "node_type": "attribute", "path": ".", "required": True},
{"param_name": "description", "node_type": "element", "path": "description", "required": False},
{"param_name": "cron_expression", "node_type": "attribute", "path": ".", "required": True},
{"param_name": "execution_type", "node_type": "attribute", "path": ".", "required": True},
{"param_name": "workflow_id", "node_type": "element", "path": "workflow_id", "required": False},
{"param_name": "workflow_input", "node_type": "element", "path": "workflow_input", "required": False},
{"param_name": "agent_prompt", "node_type": "element", "path": "agent_prompt", "required": False}
],
example='''
<function_calls>
<invoke name="create_scheduled_trigger">
<parameter name="name">Daily Report Generation</parameter>
<parameter name="description">Generates daily reports every morning at 9 AM</parameter>
<parameter name="cron_expression">0 9 * * *</parameter>
<parameter name="execution_type">workflow</parameter>
<parameter name="workflow_id">workflow-123</parameter>
<parameter name="workflow_input">{"report_type": "daily", "include_charts": true}</parameter>
</invoke>
</function_calls>
'''
)
async def create_scheduled_trigger(
self,
name: str,
cron_expression: str,
execution_type: str = "agent",
description: Optional[str] = None,
workflow_id: Optional[str] = None,
workflow_input: Optional[Dict[str, Any]] = None,
agent_prompt: Optional[str] = None
) -> ToolResult:
try:
if execution_type not in ["workflow", "agent"]:
return self.fail_response("execution_type must be either 'workflow' or 'agent'")
if execution_type == "workflow" and not workflow_id:
return self.fail_response("workflow_id is required when execution_type is 'workflow'")
if execution_type == "agent" and not agent_prompt:
return self.fail_response("agent_prompt is required when execution_type is 'agent'")
if execution_type == "workflow":
client = await self.db.client
workflow_result = await client.table('agent_workflows').select('*').eq('id', workflow_id).eq('agent_id', self.agent_id).execute()
if not workflow_result.data:
return self.fail_response(f"Workflow {workflow_id} not found or doesn't belong to this agent")
workflow = workflow_result.data[0]
if workflow['status'] != 'active':
return self.fail_response(f"Workflow '{workflow['name']}' is not active. Please activate it first.")
trigger_config = {
"cron_expression": cron_expression,
"execution_type": execution_type,
"provider_id": "schedule"
}
if execution_type == "workflow":
trigger_config["workflow_id"] = workflow_id
if workflow_input:
trigger_config["workflow_input"] = workflow_input
else:
trigger_config["agent_prompt"] = agent_prompt
trigger_db = DBConnection()
trigger_manager = TriggerManager(trigger_db)
await trigger_manager.load_provider_definitions()
try:
trigger_config_obj = await trigger_manager.create_trigger(
agent_id=self.agent_id,
provider_id="schedule",
name=name,
config=trigger_config,
description=description
)
result_message = f"Scheduled trigger '{name}' created successfully!\n\n"
result_message += f"**Schedule**: {cron_expression}\n"
result_message += f"**Type**: {execution_type.capitalize()} execution\n"
if execution_type == "workflow":
result_message += f"**Workflow**: {workflow['name']}\n"
if workflow_input:
result_message += f"**Input Data**: {json.dumps(workflow_input, indent=2)}\n"
else:
result_message += f"**Prompt**: {agent_prompt}\n"
result_message += f"\nThe trigger is now active and will run according to the schedule."
return self.success_response({
"message": result_message,
"trigger": {
"id": trigger_config_obj.trigger_id,
"name": trigger_config_obj.name,
"description": trigger_config_obj.description,
"cron_expression": cron_expression,
"execution_type": execution_type,
"is_active": trigger_config_obj.is_active,
"created_at": trigger_config_obj.created_at.isoformat()
}
})
except ValueError as ve:
return self.fail_response(f"Validation error: {str(ve)}")
except Exception as e:
logger.error(f"Error creating trigger through manager: {str(e)}")
return self.fail_response(f"Failed to create trigger: {str(e)}")
except Exception as e:
logger.error(f"Error creating scheduled trigger: {str(e)}")
return self.fail_response(f"Error creating scheduled trigger: {str(e)}")
@openapi_schema({
"type": "function",
"function": {
"name": "get_scheduled_triggers",
"description": "Get all scheduled triggers for the current agent. Shows when the agent will run automatically.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
})
@xml_schema(
tag_name="get-scheduled-triggers",
mappings=[],
example='''
<function_calls>
<invoke name="get_scheduled_triggers">
</invoke>
</function_calls>
'''
)
async def get_scheduled_triggers(self) -> ToolResult:
try:
from triggers.core import TriggerType
trigger_db = DBConnection()
trigger_manager = TriggerManager(trigger_db)
triggers = await trigger_manager.get_agent_triggers(self.agent_id)
schedule_triggers = [t for t in triggers if t.trigger_type == TriggerType.SCHEDULE]
if not schedule_triggers:
return self.success_response({
"message": "No scheduled triggers found for this agent.",
"triggers": []
})
client = await self.db.client
workflows = {}
for trigger in schedule_triggers:
if trigger.config.get("execution_type") == "workflow" and trigger.config.get("workflow_id"):
workflow_id = trigger.config["workflow_id"]
if workflow_id not in workflows:
workflow_result = await client.table('agent_workflows').select('name').eq('id', workflow_id).execute()
if workflow_result.data:
workflows[workflow_id] = workflow_result.data[0]['name']
formatted_triggers = []
for trigger in schedule_triggers:
formatted = {
"id": trigger.trigger_id,
"name": trigger.name,
"description": trigger.description,
"cron_expression": trigger.config.get("cron_expression"),
"execution_type": trigger.config.get("execution_type", "agent"),
"is_active": trigger.is_active,
"created_at": trigger.created_at.isoformat()
}
if trigger.config.get("execution_type") == "workflow":
workflow_id = trigger.config.get("workflow_id")
formatted["workflow_name"] = workflows.get(workflow_id, "Unknown Workflow")
formatted["workflow_input"] = trigger.config.get("workflow_input")
else:
formatted["agent_prompt"] = trigger.config.get("agent_prompt")
formatted_triggers.append(formatted)
return self.success_response({
"message": f"Found {len(formatted_triggers)} scheduled trigger(s)",
"triggers": formatted_triggers
})
except Exception as e:
logger.error(f"Error getting scheduled triggers: {str(e)}")
return self.fail_response(f"Error getting scheduled triggers: {str(e)}")
@openapi_schema({
"type": "function",
"function": {
"name": "delete_scheduled_trigger",
"description": "Delete a scheduled trigger. The agent will no longer run automatically at the scheduled time.",
"parameters": {
"type": "object",
"properties": {
"trigger_id": {
"type": "string",
"description": "ID of the trigger to delete"
}
},
"required": ["trigger_id"]
}
}
})
@xml_schema(
tag_name="delete-scheduled-trigger",
mappings=[
{"param_name": "trigger_id", "node_type": "attribute", "path": ".", "required": True}
],
example='''
<function_calls>
<invoke name="delete_scheduled_trigger">
<parameter name="trigger_id">trigger-123</parameter>
</invoke>
</function_calls>
'''
)
async def delete_scheduled_trigger(self, trigger_id: str) -> ToolResult:
try:
trigger_db = DBConnection()
trigger_manager = TriggerManager(trigger_db)
trigger_config = await trigger_manager.get_trigger(trigger_id)
if not trigger_config:
return self.fail_response("Trigger not found")
if trigger_config.agent_id != self.agent_id:
return self.fail_response("This trigger doesn't belong to the current agent")
success = await trigger_manager.delete_trigger(trigger_id)
if success:
return self.success_response({
"message": f"Scheduled trigger '{trigger_config.name}' deleted successfully",
"trigger_id": trigger_id
})
else:
return self.fail_response("Failed to delete trigger")
except Exception as e:
logger.error(f"Error deleting scheduled trigger: {str(e)}")
return self.fail_response(f"Error deleting scheduled trigger: {str(e)}")
@openapi_schema({
"type": "function",
"function": {
"name": "toggle_scheduled_trigger",
"description": "Enable or disable a scheduled trigger. Disabled triggers won't run until re-enabled.",
"parameters": {
"type": "object",
"properties": {
"trigger_id": {
"type": "string",
"description": "ID of the trigger to toggle"
},
"is_active": {
"type": "boolean",
"description": "Whether to enable (true) or disable (false) the trigger"
}
},
"required": ["trigger_id", "is_active"]
}
}
})
@xml_schema(
tag_name="toggle-scheduled-trigger",
mappings=[
{"param_name": "trigger_id", "node_type": "attribute", "path": ".", "required": True},
{"param_name": "is_active", "node_type": "attribute", "path": ".", "required": True}
],
example='''
<function_calls>
<invoke name="toggle_scheduled_trigger">
<parameter name="trigger_id">trigger-123</parameter>
<parameter name="is_active">false</parameter>
</invoke>
</function_calls>
'''
)
async def toggle_scheduled_trigger(self, trigger_id: str, is_active: bool) -> ToolResult:
try:
trigger_db = DBConnection()
trigger_manager = TriggerManager(trigger_db)
trigger_config = await trigger_manager.get_trigger(trigger_id)
if not trigger_config:
return self.fail_response("Trigger not found")
if trigger_config.agent_id != self.agent_id:
return self.fail_response("This trigger doesn't belong to the current agent")
updated_config = await trigger_manager.update_trigger(
trigger_id=trigger_id,
is_active=is_active
)
if updated_config:
status = "enabled" if is_active else "disabled"
return self.success_response({
"message": f"Scheduled trigger '{updated_config.name}' has been {status}",
"trigger": {
"id": updated_config.trigger_id,
"name": updated_config.name,
"is_active": updated_config.is_active
}
})
else:
return self.fail_response("Failed to update trigger")
except Exception as e:
logger.error(f"Error toggling scheduled trigger: {str(e)}")
return self.fail_response(f"Error toggling scheduled trigger: {str(e)}")

View File

@ -24,58 +24,42 @@ class WorkflowTool(AgentBuilderBaseTool):
agent_config = extract_agent_config(agent_data, version_data)
agentpress_tools = agent_config.get('agentpress_tools', {})
configured_mcps = agent_config.get('configured_mcps', [])
custom_mcps = agent_config.get('custom_mcps', [])
available_tools = []
if agentpress_tools.get('sb_shell_tool', {}).get('enabled', False):
available_tools.append('execute_command')
if agentpress_tools.get('sb_files_tool', {}).get('enabled', False):
available_tools.extend(['create_file', 'str_replace', 'full_file_rewrite', 'delete_file'])
if agentpress_tools.get('sb_browser_tool', {}).get('enabled', False):
available_tools.extend(['browser_navigate_to', 'browser_take_screenshot'])
if agentpress_tools.get('sb_vision_tool', {}).get('enabled', False):
available_tools.append('see_image')
if agentpress_tools.get('sb_deploy_tool', {}).get('enabled', False):
available_tools.append('deploy')
if agentpress_tools.get('sb_expose_tool', {}).get('enabled', False):
available_tools.append('expose_port')
if agentpress_tools.get('web_search_tool', {}).get('enabled', False):
available_tools.append('web_search')
if agentpress_tools.get('data_providers_tool', {}).get('enabled', False):
available_tools.extend(['get_data_provider_endpoints', 'execute_data_provider_call'])
tool_mapping = {
'sb_shell_tool': ['execute_command'],
'sb_files_tool': ['create_file', 'str_replace', 'full_file_rewrite', 'delete_file'],
'sb_browser_tool': ['browser_navigate_to', 'browser_take_screenshot'],
'sb_vision_tool': ['see_image'],
'sb_deploy_tool': ['deploy'],
'sb_expose_tool': ['expose_port'],
'web_search_tool': ['web_search'],
'data_providers_tool': ['get_data_provider_endpoints', 'execute_data_provider_call']
}
# Add MCP tools
all_mcps = []
if configured_mcps:
all_mcps.extend(configured_mcps)
if custom_mcps:
all_mcps.extend(custom_mcps)
agentpress_tools = agent_config.get('agentpress_tools', {})
for tool_key, tool_names in tool_mapping.items():
if agentpress_tools.get(tool_key, {}).get('enabled', False):
available_tools.extend(tool_names)
for mcp in all_mcps:
qualified_name = mcp.get('qualifiedName', '')
enabled_tools_list = mcp.get('enabledTools', [])
if qualified_name == 'exa' and ('search' in enabled_tools_list or not enabled_tools_list):
available_tools.append('web_search_exa')
elif qualified_name.startswith('@smithery-ai/github'):
for tool in enabled_tools_list:
available_tools.append(tool.replace('-', '_'))
elif qualified_name.startswith('pipedream:'):
# Handle Pipedream tools
for tool in enabled_tools_list:
available_tools.append(f"pipedream_{tool}")
elif qualified_name.startswith('custom_'):
for tool in enabled_tools_list:
available_tools.append(f"{qualified_name}_{tool}")
else:
# Generic MCP tools
for tool in enabled_tools_list:
available_tools.append(f"mcp_{qualified_name.replace(':', '_').replace('@', '').replace('/', '_').replace('-', '_')}_{tool}")
configured_mcps = agent_config.get('configured_mcps', [])
for mcp in configured_mcps:
enabled_tools = mcp.get('enabledTools', [])
available_tools.extend(enabled_tools)
return available_tools
custom_mcps = agent_config.get('custom_mcps', [])
for mcp in custom_mcps:
enabled_tools = mcp.get('enabledTools', [])
available_tools.extend(enabled_tools)
seen = set()
unique_tools = []
for tool in available_tools:
if tool not in seen:
seen.add(tool)
unique_tools.append(tool)
return unique_tools
except Exception as e:
logger.error(f"Error getting available tools for agent {self.agent_id}: {e}")
@ -99,113 +83,7 @@ class WorkflowTool(AgentBuilderBaseTool):
validate_step_list(steps)
return errors
@openapi_schema({
"type": "function",
"function": {
"name": "get_available_tools",
"description": "Get all tools available to the current agent. Use this to see what tools can be used in workflow steps.",
"parameters": {
"type": "object",
"properties": {
"categorized": {
"type": "boolean",
"description": "Whether to return tools organized by category (AgentPress tools, MCP tools, etc.)",
"default": False
}
},
"required": []
}
}
})
@xml_schema(
tag_name="get-available-tools",
mappings=[
{"param_name": "categorized", "node_type": "attribute", "path": ".", "required": False}
],
example='''
<function_calls>
<invoke name="get_available_tools">
<parameter name="categorized">true</parameter>
</invoke>
</function_calls>
'''
)
async def get_available_tools(self, categorized: bool = False) -> ToolResult:
try:
if categorized:
client = await self.db.client
agent_result = await client.table('agents').select('*, agent_versions!current_version_id(*)').eq('agent_id', self.agent_id).execute()
if not agent_result.data:
return self.fail_response("Agent not found")
agent_data = agent_result.data[0]
version_data = agent_data.get('agent_versions')
agent_config = extract_agent_config(agent_data, version_data)
agentpress_tools = agent_config.get('agentpress_tools', {})
configured_mcps = agent_config.get('configured_mcps', [])
custom_mcps = agent_config.get('custom_mcps', [])
categories = {
"agentpress_tools": [],
"mcp_tools": [],
"custom_mcp_tools": []
}
tool_mapping = {
'sb_shell_tool': ['execute_command'],
'sb_files_tool': ['create_file', 'str_replace', 'full_file_rewrite', 'delete_file'],
'sb_browser_tool': ['browser_navigate_to', 'browser_take_screenshot'],
'sb_vision_tool': ['see_image'],
'sb_deploy_tool': ['deploy'],
'sb_expose_tool': ['expose_port'],
'web_search_tool': ['web_search'],
'data_providers_tool': ['get_data_provider_endpoints', 'execute_data_provider_call']
}
for tool_key, tool_names in tool_mapping.items():
if agentpress_tools.get(tool_key, {}).get('enabled', False):
categories["agentpress_tools"].extend(tool_names)
for mcp in configured_mcps:
qualified_name = mcp.get('qualifiedName', '')
enabled_tools = mcp.get('enabledTools', [])
categories["mcp_tools"].append({
"server": qualified_name,
"tools": enabled_tools
})
for mcp in custom_mcps:
qualified_name = mcp.get('qualifiedName', '')
enabled_tools = mcp.get('enabledTools', [])
categories["custom_mcp_tools"].append({
"server": qualified_name,
"tools": enabled_tools
})
debug_info = {
"agent_id": self.agent_id,
"has_version_data": version_data is not None,
"agentpress_tools_raw": agentpress_tools,
"configured_mcps_count": len(configured_mcps),
"custom_mcps_count": len(custom_mcps)
}
return self.success_response({
"message": "Available tools by category",
"categories": categories,
"debug": debug_info
})
else:
available_tools = await self._get_available_tools_for_agent()
return self.success_response({
"message": f"Found {len(available_tools)} available tools",
"tools": available_tools
})
except Exception as e:
return self.fail_response(f"Error getting available tools: {str(e)}")
@openapi_schema({
"type": "function",

View File

@ -389,8 +389,8 @@ AVAILABLE TOOLS:
IMPORTANT TOOL USAGE:
- When a step specifies a tool, that tool MUST be used
- If the specified tool is not available, adapt using similar available tools
- For example, if "web_search_exa" is specified but not available, use "web_search" instead
- If the specified tool is not available, explain what you would do instead
- Use only the tools that are listed as available
Current input data: {json.dumps(input_data) if input_data else 'None provided'}
@ -428,11 +428,9 @@ async def execute_agent_workflow(
if workflow['status'] != 'active':
raise HTTPException(status_code=400, detail="Workflow is not active")
# Get steps from the new JSON format or fallback to old format
if workflow.get('steps'):
steps_json = workflow['steps']
else:
# Fallback to old workflow_steps table format
workflow_steps_result = await client.table('workflow_steps').select('*').eq('workflow_id', workflow_id).order('step_order').execute()
steps_json = []
for step_data in workflow_steps_result.data:
@ -479,7 +477,6 @@ async def execute_agent_workflow(
if agentpress_tools.get('data_providers_tool', {}).get('enabled', False):
available_tools.extend(['get_data_provider_endpoints', 'execute_data_provider_call'])
# Check MCP tools
all_mcps = []
if agent_config.get('configured_mcps'):
all_mcps.extend(agent_config['configured_mcps'])
@ -487,17 +484,9 @@ async def execute_agent_workflow(
all_mcps.extend(agent_config['custom_mcps'])
for mcp in all_mcps:
qualified_name = mcp.get('qualifiedName', '')
enabled_tools_list = mcp.get('enabledTools', [])
if qualified_name == 'exa' and ('search' in enabled_tools_list or not enabled_tools_list):
available_tools.append('web_search_exa')
elif qualified_name.startswith('@smithery-ai/github'):
for tool in enabled_tools_list:
available_tools.append(tool.replace('-', '_'))
elif qualified_name.startswith('custom_'):
for tool in enabled_tools_list:
available_tools.append(f"{qualified_name}_{tool}")
available_tools.extend(enabled_tools_list)
workflow_prompt = build_workflow_system_prompt(workflow, steps_json, execution_data.input_data, available_tools)
enhanced_system_prompt = f"""{agent_config['system_prompt']}

View File

@ -405,13 +405,9 @@ async def handle_qstash_webhook(request: Request):
@router.post("/schedule/webhook")
async def handle_schedule_webhook(request: Request):
try:
logger.info("Schedule webhook received from Pipedream")
body = await request.body()
headers = dict(request.headers)
logger.debug(f"Schedule webhook body: {body[:500]}...")
logger.debug(f"Schedule webhook headers: {headers}")
try:
if body:
data = await request.json()

View File

@ -16,6 +16,7 @@ import { AgentToolsConfiguration } from '../../../../../components/agents/agent-
import { AgentPreview } from '../../../../../components/agents/agent-preview';
import { getAgentAvatar } from '../../../../../lib/utils/get-agent-style';
import { EditableText } from '@/components/ui/editable';
import { ExpandableMarkdownEditor } from '@/components/ui/expandable-markdown-editor';
import { StylePicker } from '../../../../../components/agents/style-picker';
import { useSidebar } from '@/components/ui/sidebar';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
@ -328,23 +329,22 @@ export default function AgentConfigurationPage() {
<EditableText
value={formData.description}
onSave={(value) => handleFieldChange('description', value)}
className="text-muted-foreground text-sm md:text-base"
className="text-muted-foreground text-sm"
placeholder="Click to add description..."
/>
</div>
</div>
<div className='flex flex-col mt-6 md:mt-8'>
<div className='text-sm font-semibold text-muted-foreground mb-2'>Instructions</div>
<EditableText
value={formData.system_prompt}
onSave={(value) => handleFieldChange('system_prompt', value)}
className='bg-transparent hover:bg-transparent border-none focus-visible:ring-0 shadow-none text-sm md:text-base'
placeholder='Click to set system instructions...'
multiline={true}
minHeight="150px"
/>
</div>
<div className='flex flex-col mt-6 md:mt-8'>
<div className='flex items-center justify-between mb-2'>
<div className='text-sm font-semibold text-muted-foreground'>Instructions</div>
</div>
<ExpandableMarkdownEditor
value={formData.system_prompt}
onSave={(value) => handleFieldChange('system_prompt', value)}
placeholder='Click to set system instructions...'
title='System Instructions'
/>
</div>
<div ref={accordionRef} className="mt-6 border-t">
<Accordion

View File

@ -39,7 +39,7 @@ interface PipedreamConnectorProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onComplete: (profileId: string, selectedTools: string[], appName: string, appSlug: string) => void;
mode?: 'full' | 'profile-only'; // Add mode prop
mode?: 'full' | 'profile-only';
}
interface PipedreamTool {

View File

@ -20,6 +20,7 @@ import {
import { TriggerConfiguration } from './types';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { getTriggerIcon } from './utils';
import { truncateString } from '@/lib/utils';
interface ConfiguredTriggersListProps {
triggers: TriggerConfiguration[];
@ -72,7 +73,7 @@ export const ConfiguredTriggersList: React.FC<ConfiguredTriggersListProps> = ({
{trigger.description && (
<p className="text-xs text-muted-foreground truncate">
{trigger.description}
{truncateString(trigger.description, 50)}
</p>
)}

View File

@ -0,0 +1,147 @@
import { useEffect, useState } from "react";
import { Textarea } from "./textarea";
import { cn } from "@/lib/utils";
import { Edit2 } from "lucide-react";
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
interface EditableMarkdownProps {
value: string;
onSave: (value: string) => void;
className?: string;
placeholder?: string;
minHeight?: string;
}
export const EditableMarkdown: React.FC<EditableMarkdownProps> = ({
value,
onSave,
className = '',
placeholder = 'Click to edit...',
minHeight = '150px'
}) => {
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(value);
useEffect(() => {
setEditValue(value);
}, [value]);
const handleSave = () => {
onSave(editValue);
setIsEditing(false);
};
const handleCancel = () => {
setEditValue(value);
setIsEditing(false);
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
handleCancel();
} else if (e.key === 'Enter' && e.metaKey) {
handleSave();
}
};
if (isEditing) {
return (
<div className="space-y-1">
<Textarea
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleKeyDown}
onBlur={handleSave}
autoFocus
className={cn(
'border-none shadow-none px-0 focus-visible:ring-0 bg-transparent resize-none',
className
)}
style={{
fontSize: 'inherit',
fontWeight: 'inherit',
lineHeight: 'inherit',
minHeight
}}
/>
<div className="text-xs text-muted-foreground/50 px-0">
Markdown supported Cmd+Enter to save Esc to cancel
</div>
</div>
);
}
return (
<div
className={cn(
'group bg-transparent cursor-pointer relative rounded px-2 py-1 -mx-2 -my-1 transition-colors hover:bg-muted/50',
className
)}
onClick={() => setIsEditing(true)}
>
<div
className={cn(
value ? '' : 'text-muted-foreground italic',
'prose prose-sm dark:prose-invert max-w-none'
)}
style={{ minHeight }}
>
{value ? (
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
// Custom styling for markdown elements
h1: ({ children }) => <h1 className="text-lg font-semibold mb-2">{children}</h1>,
h2: ({ children }) => <h2 className="text-base font-semibold mb-2">{children}</h2>,
h3: ({ children }) => <h3 className="text-sm font-semibold mb-1">{children}</h3>,
p: ({ children }) => <p className="mb-2 last:mb-0">{children}</p>,
ul: ({ children }) => <ul className="list-disc list-inside mb-2 space-y-1">{children}</ul>,
ol: ({ children }) => <ol className="list-decimal list-inside mb-2 space-y-1">{children}</ol>,
li: ({ children }) => <li className="text-sm">{children}</li>,
code: ({ children, className }) => {
const isInline = !className?.includes('language-');
return isInline ? (
<code className="bg-muted px-1 py-0.5 rounded text-xs font-mono">{children}</code>
) : (
<code className={cn('block bg-muted p-2 rounded text-xs font-mono overflow-x-auto', className)}>
{children}
</code>
);
},
pre: ({ children }) => <pre className="bg-muted p-2 rounded text-xs font-mono overflow-x-auto mb-2">{children}</pre>,
blockquote: ({ children }) => <blockquote className="border-l-4 border-muted-foreground/20 pl-4 italic mb-2">{children}</blockquote>,
strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
em: ({ children }) => <em className="italic">{children}</em>,
hr: () => <hr className="my-4 border-muted-foreground/20" />,
table: ({ children }) => (
<div className="overflow-x-auto">
<table className="min-w-full border-collapse border border-muted-foreground/20 mb-2">
{children}
</table>
</div>
),
th: ({ children }) => (
<th className="border border-muted-foreground/20 px-2 py-1 bg-muted font-semibold text-left text-xs">
{children}
</th>
),
td: ({ children }) => (
<td className="border border-muted-foreground/20 px-2 py-1 text-xs">
{children}
</td>
),
}}
>
{value}
</ReactMarkdown>
) : (
<div className="text-muted-foreground italic" style={{ minHeight }}>
{placeholder}
</div>
)}
</div>
<Edit2 className="h-3 w-3 opacity-0 group-hover:opacity-50 absolute top-1 right-1 transition-opacity" />
</div>
);
};

View File

@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { Textarea } from "./textarea";
import { Input } from "./input";
import { Button } from "../home/ui/button";
import { cn } from "@/lib/utils";
import { cn, truncateString } from "@/lib/utils";
import { Edit2 } from "lucide-react";
interface EditableTextProps {
@ -60,7 +60,7 @@ export const EditableText: React.FC<EditableTextProps> = ({
onBlur={handleSave}
autoFocus
className={cn(
'border-none shadow-none px-0 focus-visible:ring-0 bg-transparent',
'text-sm border-none shadow-none px-0 focus-visible:ring-0 bg-transparent',
multiline ? 'resize-none' : '',
multiline && minHeight ? `min-h-[${minHeight}]` : '',
className
@ -88,7 +88,7 @@ export const EditableText: React.FC<EditableTextProps> = ({
value ? '' : 'text-muted-foreground italic',
multiline && minHeight ? `min-h-[${minHeight}]` : ''
)} style={multiline && minHeight ? { minHeight } : {}}>
{value || placeholder}
{truncateString(value, 50) || placeholder}
</div>
<Edit2 className="h-3 w-3 opacity-0 group-hover:opacity-50 absolute top-1 right-1 transition-opacity" />
</div>

View File

@ -0,0 +1,227 @@
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Textarea } from "./textarea";
import { cn } from "@/lib/utils";
import { Edit2, Expand, Save, X } from "lucide-react";
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
interface ExpandableMarkdownEditorProps {
value: string;
onSave: (value: string) => void;
className?: string;
placeholder?: string;
title?: string;
}
export const ExpandableMarkdownEditor: React.FC<ExpandableMarkdownEditorProps> = ({
value,
onSave,
className = '',
placeholder = 'Click to edit...',
title = 'Edit Instructions'
}) => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(value);
useEffect(() => {
setEditValue(value);
}, [value]);
const handleSave = () => {
onSave(editValue);
setIsEditing(false);
setIsDialogOpen(false);
};
const handleCancel = () => {
setEditValue(value);
setIsEditing(false);
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
handleCancel();
} else if (e.key === 'Enter' && e.metaKey) {
handleSave();
}
};
const openDialog = () => {
setIsDialogOpen(true);
setIsEditing(false);
};
const startEditing = () => {
setIsEditing(true);
};
const renderMarkdown = (content: string, isPreview = false) => (
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h1: ({ children }) => <h1 className="text-lg font-semibold mb-2">{children}</h1>,
h2: ({ children }) => <h2 className="text-base font-semibold mb-2">{children}</h2>,
h3: ({ children }) => <h3 className="text-sm font-semibold mb-1">{children}</h3>,
p: ({ children }) => <p className="mb-2 last:mb-0">{children}</p>,
ul: ({ children }) => <ul className="list-disc list-inside mb-2 space-y-1">{children}</ul>,
ol: ({ children }) => <ol className="list-decimal list-inside mb-2 space-y-1">{children}</ol>,
li: ({ children }) => <li className="text-sm">{children}</li>,
code: ({ children, className }) => {
const isInline = !className?.includes('language-');
return isInline ? (
<code className="bg-muted px-1 py-0.5 rounded text-xs font-mono">{children}</code>
) : (
<code className={cn('block bg-muted p-2 rounded text-xs font-mono overflow-x-auto', className)}>
{children}
</code>
);
},
pre: ({ children }) => <pre className="bg-muted p-2 rounded text-xs font-mono overflow-x-auto mb-2">{children}</pre>,
blockquote: ({ children }) => <blockquote className="border-l-4 border-muted-foreground/20 pl-4 italic mb-2">{children}</blockquote>,
strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
em: ({ children }) => <em className="italic">{children}</em>,
hr: () => <hr className="my-4 border-muted-foreground/20" />,
table: ({ children }) => (
<div className="overflow-x-auto">
<table className="min-w-full border-collapse border border-muted-foreground/20 mb-2">
{children}
</table>
</div>
),
th: ({ children }) => (
<th className="border border-muted-foreground/20 px-2 py-1 bg-muted font-semibold text-left text-xs">
{children}
</th>
),
td: ({ children }) => (
<td className="border border-muted-foreground/20 px-2 py-1 text-xs">
{children}
</td>
),
}}
>
{content}
</ReactMarkdown>
);
return (
<>
<div className={cn('relative', className)}>
<div
className="group relative pb-4 border rounded-lg bg-muted/30 hover:opacity-80 transition-colors cursor-pointer overflow-hidden"
onClick={openDialog}
>
<div className="p-4 max-h-32 overflow-hidden">
{value ? (
<div className="prose prose-sm dark:prose-invert max-w-none text-sm">
{renderMarkdown(value, true)}
</div>
) : (
<div className="text-muted-foreground italic text-sm">
{placeholder}
</div>
)}
</div>
{value && value.length > 200 && (
<div className="absolute bottom-2 left-4 text-xs text-muted-foreground/60 z-10">
.........
</div>
)}
<div className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
<Button
size="sm"
className="h-6 w-6 p-0 shadow-sm"
onClick={(e) => {
e.stopPropagation();
openDialog();
}}
>
<Expand className="h-3 w-3" />
</Button>
</div>
</div>
</div>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogContent className="max-w-4xl max-h-[80vh] w-[95vw] md:w-full flex flex-col">
<DialogHeader>
<DialogTitle className="flex items-center justify-between">
<span>{title}</span>
{!isEditing && (
<Button
size="sm"
variant="outline"
onClick={startEditing}
className="h-7 mr-4"
>
<Edit2 className="h-3 w-3" />
Edit
</Button>
)}
</DialogTitle>
</DialogHeader>
<div className="flex-1 overflow-hidden">
{isEditing ? (
<div className="h-full flex flex-col gap-2">
<ScrollArea className="flex-1 h-[500px]">
<textarea
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleKeyDown}
className="w-full h-[500px] rounded-xl bg-muted/30 p-4 resize-none"
style={{ minHeight: '300px' }}
/>
</ScrollArea>
<div className="text-xs text-muted-foreground/50 flex-shrink-0">
Markdown supported Cmd+Enter to save Esc to cancel
</div>
</div>
) : (
<ScrollArea className="flex-1 h-[500px]">
<div className="pr-4">
{value ? (
<div className="prose prose-sm dark:prose-invert max-w-none">
{renderMarkdown(value)}
</div>
) : (
<div className="text-muted-foreground italic text-center py-8">
{placeholder}
</div>
)}
</div>
</ScrollArea>
)}
</div>
{isEditing && (
<DialogFooter>
<Button
size="sm"
variant="outline"
onClick={handleCancel}
className="h-8"
>
<X className="h-3 w-3" />
Cancel
</Button>
<Button
size="sm"
variant="default"
onClick={handleSave}
className="h-8"
>
<Save className="h-3 w-3" />
Save
</Button>
</DialogFooter>
)}
</DialogContent>
</Dialog>
</>
);
};

View File

@ -0,0 +1,66 @@
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { cn } from '@/lib/utils';
interface MarkdownRendererProps {
content: string;
className?: string;
}
export const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({
content,
className = ''
}) => {
return (
<div className={cn('prose prose-sm dark:prose-invert max-w-none', className)}>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
// Custom styling for markdown elements
h1: ({ children }) => <h1 className="text-lg font-semibold mb-2">{children}</h1>,
h2: ({ children }) => <h2 className="text-base font-semibold mb-2">{children}</h2>,
h3: ({ children }) => <h3 className="text-sm font-semibold mb-1">{children}</h3>,
p: ({ children }) => <p className="mb-2 last:mb-0">{children}</p>,
ul: ({ children }) => <ul className="list-disc list-inside mb-2 space-y-1">{children}</ul>,
ol: ({ children }) => <ol className="list-decimal list-inside mb-2 space-y-1">{children}</ol>,
li: ({ children }) => <li className="text-sm">{children}</li>,
code: ({ children, className }) => {
const isInline = !className?.includes('language-');
return isInline ? (
<code className="bg-muted px-1 py-0.5 rounded text-xs font-mono">{children}</code>
) : (
<code className={cn('block bg-muted p-2 rounded text-xs font-mono overflow-x-auto', className)}>
{children}
</code>
);
},
pre: ({ children }) => <pre className="bg-muted p-2 rounded text-xs font-mono overflow-x-auto mb-2">{children}</pre>,
blockquote: ({ children }) => <blockquote className="border-l-4 border-muted-foreground/20 pl-4 italic mb-2">{children}</blockquote>,
strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
em: ({ children }) => <em className="italic">{children}</em>,
hr: () => <hr className="my-4 border-muted-foreground/20" />,
table: ({ children }) => (
<div className="overflow-x-auto">
<table className="min-w-full border-collapse border border-muted-foreground/20 mb-2">
{children}
</table>
</div>
),
th: ({ children }) => (
<th className="border border-muted-foreground/20 px-2 py-1 bg-muted font-semibold text-left text-xs">
{children}
</th>
),
td: ({ children }) => (
<td className="border border-muted-foreground/20 px-2 py-1 text-xs">
{children}
</td>
),
}}
>
{content}
</ReactMarkdown>
</div>
);
};