vibesurf 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vibesurf might be problematic. Click here for more details.

Files changed (70) hide show
  1. vibe_surf/__init__.py +12 -0
  2. vibe_surf/_version.py +34 -0
  3. vibe_surf/agents/__init__.py +0 -0
  4. vibe_surf/agents/browser_use_agent.py +1106 -0
  5. vibe_surf/agents/prompts/__init__.py +1 -0
  6. vibe_surf/agents/prompts/vibe_surf_prompt.py +176 -0
  7. vibe_surf/agents/report_writer_agent.py +360 -0
  8. vibe_surf/agents/vibe_surf_agent.py +1632 -0
  9. vibe_surf/backend/__init__.py +0 -0
  10. vibe_surf/backend/api/__init__.py +3 -0
  11. vibe_surf/backend/api/activity.py +243 -0
  12. vibe_surf/backend/api/config.py +740 -0
  13. vibe_surf/backend/api/files.py +322 -0
  14. vibe_surf/backend/api/models.py +257 -0
  15. vibe_surf/backend/api/task.py +300 -0
  16. vibe_surf/backend/database/__init__.py +13 -0
  17. vibe_surf/backend/database/manager.py +129 -0
  18. vibe_surf/backend/database/models.py +164 -0
  19. vibe_surf/backend/database/queries.py +922 -0
  20. vibe_surf/backend/database/schemas.py +100 -0
  21. vibe_surf/backend/llm_config.py +182 -0
  22. vibe_surf/backend/main.py +137 -0
  23. vibe_surf/backend/migrations/__init__.py +16 -0
  24. vibe_surf/backend/migrations/init_db.py +303 -0
  25. vibe_surf/backend/migrations/seed_data.py +236 -0
  26. vibe_surf/backend/shared_state.py +601 -0
  27. vibe_surf/backend/utils/__init__.py +7 -0
  28. vibe_surf/backend/utils/encryption.py +164 -0
  29. vibe_surf/backend/utils/llm_factory.py +225 -0
  30. vibe_surf/browser/__init__.py +8 -0
  31. vibe_surf/browser/agen_browser_profile.py +130 -0
  32. vibe_surf/browser/agent_browser_session.py +416 -0
  33. vibe_surf/browser/browser_manager.py +296 -0
  34. vibe_surf/browser/utils.py +790 -0
  35. vibe_surf/browser/watchdogs/__init__.py +0 -0
  36. vibe_surf/browser/watchdogs/action_watchdog.py +291 -0
  37. vibe_surf/browser/watchdogs/dom_watchdog.py +954 -0
  38. vibe_surf/chrome_extension/background.js +558 -0
  39. vibe_surf/chrome_extension/config.js +48 -0
  40. vibe_surf/chrome_extension/content.js +284 -0
  41. vibe_surf/chrome_extension/dev-reload.js +47 -0
  42. vibe_surf/chrome_extension/icons/convert-svg.js +33 -0
  43. vibe_surf/chrome_extension/icons/logo-preview.html +187 -0
  44. vibe_surf/chrome_extension/icons/logo.png +0 -0
  45. vibe_surf/chrome_extension/manifest.json +53 -0
  46. vibe_surf/chrome_extension/popup.html +134 -0
  47. vibe_surf/chrome_extension/scripts/api-client.js +473 -0
  48. vibe_surf/chrome_extension/scripts/main.js +491 -0
  49. vibe_surf/chrome_extension/scripts/markdown-it.min.js +3 -0
  50. vibe_surf/chrome_extension/scripts/session-manager.js +599 -0
  51. vibe_surf/chrome_extension/scripts/ui-manager.js +3687 -0
  52. vibe_surf/chrome_extension/sidepanel.html +347 -0
  53. vibe_surf/chrome_extension/styles/animations.css +471 -0
  54. vibe_surf/chrome_extension/styles/components.css +670 -0
  55. vibe_surf/chrome_extension/styles/main.css +2307 -0
  56. vibe_surf/chrome_extension/styles/settings.css +1100 -0
  57. vibe_surf/cli.py +357 -0
  58. vibe_surf/controller/__init__.py +0 -0
  59. vibe_surf/controller/file_system.py +53 -0
  60. vibe_surf/controller/mcp_client.py +68 -0
  61. vibe_surf/controller/vibesurf_controller.py +616 -0
  62. vibe_surf/controller/views.py +37 -0
  63. vibe_surf/llm/__init__.py +21 -0
  64. vibe_surf/llm/openai_compatible.py +237 -0
  65. vibesurf-0.1.0.dist-info/METADATA +97 -0
  66. vibesurf-0.1.0.dist-info/RECORD +70 -0
  67. vibesurf-0.1.0.dist-info/WHEEL +5 -0
  68. vibesurf-0.1.0.dist-info/entry_points.txt +2 -0
  69. vibesurf-0.1.0.dist-info/licenses/LICENSE +201 -0
  70. vibesurf-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ # Prompts module for VibeSurf agents
@@ -0,0 +1,176 @@
1
+ # Supervisor Agent System Prompt - Core controller role definition
2
+ SUPERVISOR_AGENT_SYSTEM_PROMPT = """
3
+ You are the VibeSurf Agent developed by [WarmShao](https://github.com/warmshao), you are a helpful browser assistant, the core controller of the browser automating. You manage todo lists, assign tasks, and coordinate the entire execution process.
4
+ Your mission is to do your best to help users vibe surfing the internet, the world and the future.
5
+
6
+ You may receive context in the user message with these keys(subset):
7
+ - User's New Request: The user's initial request with Upload Files(Optional, used for completed task or request). Always prioritize and execute the user's latest extracted request unless it's a supplement or continuation of previous tasks, in which case combine previous requests and results for informed decision-making.
8
+ - Current Todos: List of pending todo items that need completion in previous stage
9
+ - Completed Todos: List of already finished todo items with their results
10
+ - Previous Browser Execution Results: Results from previous executed browser automation tasks
11
+ - Generated Report Path: Generated report path from report writer agent
12
+ - Available Browser Tabs: Current available browser tabs(pages), format as [page_index] Page Title, Page Url and Page ID
13
+
14
+ Your responsibilities:
15
+ 1. TODO Management: Generate initial todos if none exist, or update todos based on task results
16
+ 2. Task Assignment: Decide whether to assign browser tasks (parallel/single) or report tasks(if user specified)
17
+ 3. Progress Tracking: Determine if tasks are complete and ready for summary
18
+
19
+ Todo Item Creation and Update Guidelines:
20
+ - Keep todo items simple and goal-oriented (especially for browser agents)
21
+ - Focus on WHAT you want to achieve and WHAT results you expect
22
+ - DO NOT include detailed step-by-step instructions or implementation details
23
+ - DO NOT over-split tasks into too many granular items - keep logical groupings together
24
+ - Browser agents have strong planning and execution capabilities - they only need task descriptions and desired outcomes
25
+ - If this task requires some contextual information from previous result, please also describe it in the task, such as which file paths need to be read from and etc.
26
+ - Example: "Search for latest iPhone 15 prices and return comparison data" (NOT "Go to Apple website, navigate to iPhone section, find iPhone 15, check prices...")
27
+
28
+ Available Actions:
29
+ - "simple_response": Directly return response content or answer if you think this is a simple task, such as Basic calculations or conversions or General advice or recommendations based on common knowledge and etc.
30
+ - "generate_todos": Create initial todo list (only if no todos exist)
31
+ - "update_todos": Update or Replace all remaining todos based on results and progress
32
+ - "assign_browser_task": Assign browser automation tasks
33
+ - "assign_report_task": Assign HTML report generation task
34
+ - "summary_generation": Generate final markdown summary and complete the workflow when all requirements have been met
35
+
36
+ Task Assignment Guidelines:
37
+ - Browser tasks: Use when web research, data extraction, or automation is needed
38
+ - Report tasks: Use when user explicitly requests reports or when complex data needs structured presentation.
39
+
40
+ File Processing Capabilities:
41
+ - Browser agent can read and process various file types (text, documents, images, etc.)
42
+ - Browser agent can extract information from files and perform file analysis
43
+ - When users upload files for processing, summarization, or analysis tasks, these can be assigned to browser agent
44
+ - When creating file-related tasks, always include the specific absolute file paths in the task description. The path format provided by the user is generally like this: file:///{absolute file path}, Please only use the absolute file path.
45
+ - Examples: "Read and summarize the content from file path: path/to/document.pdf", "Analyze data from uploaded file: path/to/data.csv and generate insights"
46
+
47
+ Browser Task Execution Mode Rules:
48
+ - "single": Use for tasks on user-opened pages (form filling, automation on existing pages, dependent sequential tasks). ONLY supports 1 task.
49
+ - "parallel": Use for independent research tasks (web searching, deep research, data extraction from multiple sources) that can run concurrently for efficiency.
50
+
51
+ Browser Tab Management Guidelines:
52
+
53
+ **Single Mode:**
54
+ - No need to specify page_index - automatically uses the current active page
55
+ - Browser use agent can see all available tabs for context
56
+ - Default behavior works on the currently active tab
57
+
58
+ **Parallel Mode:**
59
+ - When generating tasks_to_execute, you can optionally specify page index using format: [[page_index, todo_index], [page_index, todo_index], todo_index]
60
+ - Examples: [[1, 0], [0, 1], 2] (execute todo_item[0] on page 1, todo_item[1] on page 0, todo_item[2] on new page)
61
+ - If page_index is specified: Agent will work on that specific page (may affect user's opened web pages)
62
+ - If page_index is NOT specified: Agent will open a new blank page to work on
63
+ - IMPORTANT: Only specify page_index when:
64
+ - User explicitly requests work on already opened pages, OR
65
+ - The target page is a blank page, OR
66
+ - Task only involves simple information gathering from open pages (no automation that changes page state)
67
+ - Parallel mode agents only see their specified page or newly opened page (isolation implemented)
68
+ - Avoid specifying page_index for tasks that involve automation unless explicitly requested by user
69
+
70
+
71
+ Browser Task Execution Requirements:
72
+ - For "assign_browser_task" action, use todo item indices in "tasks_to_execute" to reference items from "todo_items"
73
+ - Format: use integer indices (0-based) to reference todo_items, or [page_index, todo_index] for specific page assignment
74
+ - This ensures efficient referencing and proper tracking of todo items without duplication
75
+
76
+ Decision Rules:
77
+ - If no todos exist: generate_todos
78
+ - If browser tasks are pending: assign_browser_task
79
+ - If all browser tasks complete and user wants report: assign_report_task
80
+ - If all tasks complete: summary_generation
81
+
82
+ IMPORTANT: For "update_todos" action, always provide the complete list of remaining todo items to replace the current todo list. This ensures proper modification and cleanup of completed or unnecessary todos.
83
+
84
+ Respond with JSON in this exact format:
85
+ {{
86
+ "reasoning": "explanation of current situation and decision",
87
+ "action": "simple_response|generate_todos|update_todos|assign_browser_task|assign_report_task|summary_generation",
88
+ "simple_response_content": "the actual response content if this is a simple response task. Just directly write the answer, no more extra content.",
89
+ "summary_content": "the comprehensive markdown summary content when action is summary_generation. Include key findings, results, and links to generated files. For local file links, use the file:// protocol format: [Report Name](file:///absolute/path/to/file.html) to ensure proper file access in browser extensions.",
90
+ "todo_items": ["complete list of remaining todos - ALWAYS include for generate_todos and update_todos actions"],
91
+ "task_type": "parallel|single (for browser tasks)",
92
+ "tasks_to_execute": ["todo indices to execute now - use 0-based indices referencing todo_items, ONLY 1 index for single mode. For parallel mode, optionally use [[page_index, todo_index], todo_index] format to specify page"]
93
+ }}
94
+
95
+ The language of your output should remain the same as the user's request, including the content of the reasoning, response, todo list, browser task and etc. in values of JSON, but the names of the keys in the JSON should remain in English.
96
+ """
97
+
98
+ REPORT_CONTENT_PROMPT = """
99
+ You are a professional report writer tasked with creating content that directly fulfills the user's request.
100
+
101
+ **User's Original Request:** {original_task}
102
+ **Report Type:** {report_type}
103
+ **Available Data:** {execution_results}
104
+
105
+ **Instructions:**
106
+ 1. Focus ONLY on what the user specifically requested - ignore technical execution details
107
+ 2. Create content that directly addresses the user's needs (comparison, analysis, research findings, etc.)
108
+ 3. DO NOT include methodology, task overview, or technical process information
109
+ 4. DO NOT mention agents, browser automation, or technical execution methods
110
+ 5. Write as if you're delivering exactly what the user asked for
111
+ 6. Use a professional, clear, and engaging style
112
+ 7. Structure content with clear sections relevant to the user's request
113
+ 8. If images or screenshots are available and would enhance the report presentation, include them in appropriate locations with proper context and descriptions
114
+
115
+ **Content Structure (adapt based on user's request):**
116
+ - Executive Summary (key findings relevant to user's request)
117
+ - Main Content (comparison, analysis, research findings - whatever user requested)
118
+ - Key Insights & Findings (specific to user's topic of interest)
119
+ - Conclusions & Recommendations (actionable insights for user's domain)
120
+
121
+ **Writing Style:**
122
+ - Professional and authoritative
123
+ - Data-driven with specific examples from the research
124
+ - Clear and concise
125
+ - Focus on subject matter insights, not process
126
+ - NO technical jargon about execution methods
127
+
128
+ Generate content that directly fulfills the user's request. Pretend you're a domain expert delivering exactly what they asked for.
129
+ """
130
+
131
+ REPORT_FORMAT_PROMPT = """
132
+ Create a beautiful, professional HTML report. Output ONLY the HTML code with no explanation or additional text.
133
+
134
+ **Content to Format:**
135
+ {report_content}
136
+
137
+ **CRITICAL: Output Rules**
138
+ - Output ONLY HTML code starting with <!DOCTYPE html>
139
+ - NO introductory text, explanations, or comments before the HTML
140
+ - NO text after the HTML code
141
+ - NO markdown code blocks or formatting
142
+ - JUST the raw HTML document
143
+
144
+ **Design Requirements:**
145
+ 1. Modern, professional HTML document with embedded CSS
146
+ 2. Clean, readable design with proper typography
147
+ 3. Responsive design principles
148
+ 4. Professional color scheme (blues, grays, whites)
149
+ 5. Proper spacing, margins, and visual hierarchy
150
+ 6. Print-friendly design
151
+ 7. Modern CSS features (flexbox, grid where appropriate)
152
+
153
+ **Structure Requirements:**
154
+ - Header with appropriate title (derived from content, NOT "Task Execution Report")
155
+ - Clearly defined sections with proper headings
156
+ - Data tables with professional styling
157
+ - Visual elements where appropriate
158
+ - Images and screenshots with proper styling, captions, and responsive design
159
+ - Clean footer
160
+
161
+ **Technical Requirements:**
162
+ - Complete HTML5 document with proper DOCTYPE
163
+ - Embedded CSS (no external dependencies)
164
+ - Responsive meta tags
165
+ - Semantic HTML elements
166
+ - Cross-browser compatibility
167
+ - Proper image handling with responsive design, appropriate sizing, and elegant layout
168
+ - Image captions and alt text for accessibility
169
+
170
+ **Title Guidelines:**
171
+ - Create title based on the actual content/comparison topic
172
+ - NOT "Task Execution Report" or similar generic titles
173
+ - Make it specific to what was researched/compared
174
+
175
+ IMPORTANT: Start your response immediately with <!DOCTYPE html> and output ONLY the HTML document.
176
+ """
@@ -0,0 +1,360 @@
1
+ import logging
2
+ import os
3
+ import time
4
+ from typing import Any, Dict, List
5
+
6
+ from browser_use.llm.base import BaseChatModel
7
+ from browser_use.llm.messages import UserMessage
8
+
9
+ from vibe_surf.agents.prompts.vibe_surf_prompt import (
10
+ REPORT_CONTENT_PROMPT,
11
+ REPORT_FORMAT_PROMPT
12
+ )
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class ReportWriterAgent:
18
+ """Agent responsible for generating HTML reports using two-phase LLM generation"""
19
+
20
+ def __init__(self, llm: BaseChatModel, workspace_dir: str):
21
+ """
22
+ Initialize ReportWriterAgent
23
+
24
+ Args:
25
+ llm: Language model for generating report content
26
+ workspace_dir: Directory to save reports
27
+ """
28
+ self.llm = llm
29
+ self.workspace_dir = workspace_dir
30
+
31
+ logger.info("📄 ReportWriterAgent initialized")
32
+
33
+ async def generate_report(self, report_data: Dict[str, Any]) -> str:
34
+ """
35
+ Generate HTML report using two-phase approach: content generation then formatting
36
+
37
+ Args:
38
+ report_data: Dictionary containing:
39
+ - original_task: The original user task
40
+ - execution_results: List of BrowserTaskResult objects
41
+ - report_type: Type of report ("summary", "detailed", "none")
42
+ - upload_files: Optional list of uploaded files
43
+
44
+ Returns:
45
+ str: Path to the generated report file
46
+ """
47
+ logger.info(f"📝 Generating {report_data.get('report_type', 'summary')} report...")
48
+
49
+ try:
50
+ # Phase 1: Generate report content
51
+ logger.info("📖 Phase 1: Generating report content...")
52
+ report_content = await self._generate_content(report_data)
53
+
54
+ # Phase 2: Format content as HTML
55
+ logger.info("🎨 Phase 2: Formatting as HTML...")
56
+ html_content = await self._format_as_html(report_content)
57
+
58
+ # Save report to file
59
+ report_filename = f"report_{int(time.time())}.html"
60
+ reports_dir = os.path.join(self.workspace_dir, "reports")
61
+ os.makedirs(reports_dir, exist_ok=True)
62
+ report_path = os.path.join(reports_dir, report_filename)
63
+
64
+ with open(report_path, 'w', encoding='utf-8') as f:
65
+ f.write(html_content)
66
+
67
+ logger.info(f"✅ Report generated successfully: {report_path}")
68
+ return report_path
69
+
70
+ except Exception as e:
71
+ logger.error(f"❌ Failed to generate report: {e}")
72
+ # Generate a simple fallback report
73
+ fallback_path = await self._generate_fallback_report(report_data)
74
+ return fallback_path
75
+
76
+ async def _generate_content(self, report_data: Dict[str, Any]) -> str:
77
+ """Generate the textual content for the report"""
78
+ # Format execution results for the prompt
79
+ results_text = self._format_execution_results(report_data.get('execution_results', []))
80
+
81
+ # Format upload files
82
+ upload_files = report_data.get('upload_files', [])
83
+ upload_files_text = "None" if not upload_files else ", ".join(upload_files)
84
+
85
+ # Generate content using the content prompt
86
+ content_prompt = REPORT_CONTENT_PROMPT.format(
87
+ original_task=report_data.get('original_task', 'No task specified'),
88
+ report_type=report_data.get('report_type', 'summary'),
89
+ upload_files=upload_files_text,
90
+ execution_results=results_text
91
+ )
92
+
93
+ response = await self.llm.ainvoke([UserMessage(content=content_prompt)])
94
+ return response.completion
95
+
96
+ async def _format_as_html(self, content: str) -> str:
97
+ """Format the content as a professional HTML document"""
98
+ format_prompt = REPORT_FORMAT_PROMPT.format(report_content=content)
99
+
100
+ response = await self.llm.ainvoke([UserMessage(content=format_prompt)])
101
+ html_content = response.completion
102
+
103
+ # Clean up the HTML content if needed
104
+ html_content = self._clean_html_content(html_content)
105
+
106
+ return html_content
107
+
108
+ def _format_execution_results(self, execution_results) -> str:
109
+ """Format execution results for the LLM prompt"""
110
+ if not execution_results:
111
+ return "No execution results available."
112
+
113
+ formatted_results = []
114
+ for i, result in enumerate(execution_results, 1):
115
+ status = "✅ Success" if result.success else "❌ Failed"
116
+
117
+ # Extract meaningful result content
118
+ result_content = "No result available"
119
+ if result.result:
120
+ # Truncate very long results but keep meaningful content
121
+ if len(result.result) > 500:
122
+ result_content = result.result[:497] + "..."
123
+ else:
124
+ result_content = result.result
125
+ elif result.error:
126
+ result_content = f"Error: {result.error}"
127
+
128
+ formatted_results.append(f"""
129
+ **Task {i}:** {result.task}
130
+ **Status:** {status}
131
+ **Agent:** {result.agent_id}
132
+ **Result:** {result_content}
133
+ **Success:** {'Yes' if result.success else 'No'}
134
+ """)
135
+
136
+ return "\n".join(formatted_results)
137
+
138
+ def _clean_html_content(self, html_content: str) -> str:
139
+ """Clean and validate HTML content"""
140
+ # Remove markdown code block markers if present
141
+ html_content = html_content.strip()
142
+ if html_content.startswith("```html"):
143
+ html_content = html_content[7:].strip()
144
+ if html_content.startswith("```"):
145
+ html_content = html_content[3:].strip()
146
+ if html_content.endswith("```"):
147
+ html_content = html_content[:-3].strip()
148
+
149
+ # Ensure it starts with <!DOCTYPE html> or <html>
150
+ if not html_content.lower().startswith(('<!doctype', '<html')):
151
+ html_content = f"""<!DOCTYPE html>
152
+ <html lang="en">
153
+ <head>
154
+ <meta charset="UTF-8">
155
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
156
+ <title>VibeSurf Task Report</title>
157
+ <style>
158
+ body {{ font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }}
159
+ .container {{ max-width: 800px; margin: 0 auto; }}
160
+ h1 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }}
161
+ h2 {{ color: #34495e; margin-top: 30px; }}
162
+ .section {{ margin: 20px 0; padding: 15px; background: #f8f9fa; border-left: 4px solid #007bff; }}
163
+ </style>
164
+ </head>
165
+ <body>
166
+ <div class="container">
167
+ {html_content}
168
+ </div>
169
+ </body>
170
+ </html>"""
171
+
172
+ return html_content
173
+
174
+ async def _generate_fallback_report(self, report_data: Dict[str, Any]) -> str:
175
+ """Generate a simple fallback report when LLM generation fails"""
176
+ logger.info("📝 Generating fallback report...")
177
+
178
+ upload_files = report_data.get('upload_files', [])
179
+ upload_files_section = ""
180
+ if upload_files:
181
+ upload_files_section = f"""
182
+ <div class="section">
183
+ <h2>Upload Files</h2>
184
+ <ul>
185
+ {"".join([f"<li>{file}</li>" for file in upload_files])}
186
+ </ul>
187
+ </div>"""
188
+
189
+ # Create a simple HTML report
190
+ html_content = f"""<!DOCTYPE html>
191
+ <html lang="en">
192
+ <head>
193
+ <meta charset="UTF-8">
194
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
195
+ <title>VibeSurf Task Report</title>
196
+ <style>
197
+ body {{
198
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
199
+ max-width: 1000px;
200
+ margin: 0 auto;
201
+ padding: 20px;
202
+ line-height: 1.6;
203
+ color: #333;
204
+ background-color: #f8f9fa;
205
+ }}
206
+ .container {{
207
+ background: white;
208
+ border-radius: 8px;
209
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
210
+ overflow: hidden;
211
+ }}
212
+ .header {{
213
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
214
+ color: white;
215
+ padding: 30px;
216
+ text-align: center;
217
+ }}
218
+ .header h1 {{
219
+ margin: 0;
220
+ font-size: 2.2em;
221
+ font-weight: 300;
222
+ }}
223
+ .section {{
224
+ margin: 0;
225
+ padding: 25px;
226
+ border-bottom: 1px solid #eee;
227
+ }}
228
+ .section:last-child {{
229
+ border-bottom: none;
230
+ }}
231
+ .section h2 {{
232
+ color: #2c3e50;
233
+ margin-top: 0;
234
+ margin-bottom: 15px;
235
+ font-size: 1.4em;
236
+ border-left: 4px solid #3498db;
237
+ padding-left: 15px;
238
+ }}
239
+ .success {{
240
+ color: #27ae60;
241
+ font-weight: 600;
242
+ }}
243
+ .error {{
244
+ color: #e74c3c;
245
+ font-weight: 600;
246
+ }}
247
+ table {{
248
+ width: 100%;
249
+ border-collapse: collapse;
250
+ margin-top: 15px;
251
+ background: white;
252
+ border-radius: 6px;
253
+ overflow: hidden;
254
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
255
+ }}
256
+ th, td {{
257
+ padding: 15px;
258
+ text-align: left;
259
+ border-bottom: 1px solid #eee;
260
+ }}
261
+ th {{
262
+ background: #34495e;
263
+ color: white;
264
+ font-weight: 600;
265
+ }}
266
+ tr:hover {{
267
+ background-color: #f8f9fa;
268
+ }}
269
+ .meta {{
270
+ background: #ecf0f1;
271
+ color: #7f8c8d;
272
+ text-align: center;
273
+ padding: 15px;
274
+ font-size: 0.9em;
275
+ }}
276
+ </style>
277
+ </head>
278
+ <body>
279
+ <div class="container">
280
+ <div class="header">
281
+ <h1>VibeSurf Task Report</h1>
282
+ <p>Generated on {time.strftime('%B %d, %Y at %H:%M:%S')}</p>
283
+ </div>
284
+
285
+ <div class="section">
286
+ <h2>Task Overview</h2>
287
+ <p><strong>Original Task:</strong> {report_data.get('original_task', 'No task specified')}</p>
288
+ <p><strong>Report Type:</strong> {report_data.get('report_type', 'summary').title()}</p>
289
+ </div>
290
+ {upload_files_section}
291
+
292
+ <div class="section">
293
+ <h2>Execution Results</h2>
294
+ <table>
295
+ <thead>
296
+ <tr>
297
+ <th>Task</th>
298
+ <th>Status</th>
299
+ <th>Agent</th>
300
+ <th>Result</th>
301
+ </tr>
302
+ </thead>
303
+ <tbody>
304
+ """
305
+
306
+ # Add execution results to table
307
+ execution_results = report_data.get('execution_results', [])
308
+ if execution_results:
309
+ for result in execution_results:
310
+ status_class = "success" if result.success else "error"
311
+ status_text = "✅ Success" if result.success else "❌ Failed"
312
+ result_text = result.result or result.error or "No result"
313
+ # Truncate long results
314
+ if len(result_text) > 150:
315
+ result_text = result_text[:147] + "..."
316
+
317
+ html_content += f"""
318
+ <tr>
319
+ <td>{result.task}</td>
320
+ <td class="{status_class}">{status_text}</td>
321
+ <td>{result.agent_id}</td>
322
+ <td>{result_text}</td>
323
+ </tr>
324
+ """
325
+ else:
326
+ html_content += """
327
+ <tr>
328
+ <td colspan="4" style="text-align: center; color: #7f8c8d; font-style: italic;">No execution results available</td>
329
+ </tr>
330
+ """
331
+
332
+ html_content += """
333
+ </tbody>
334
+ </table>
335
+ </div>
336
+
337
+ <div class="section">
338
+ <h2>Summary</h2>
339
+ <p>This report was automatically generated by VibeSurf as a fallback when the advanced report generation encountered an issue. The report contains basic information about the task execution and results.</p>
340
+ <p>For future runs, ensure that the LLM service is properly configured and accessible for enhanced report generation capabilities.</p>
341
+ </div>
342
+
343
+ <div class="meta">
344
+ Generated by VibeSurf Agent Framework
345
+ </div>
346
+ </div>
347
+ </body>
348
+ </html>"""
349
+
350
+ # Save fallback report
351
+ report_filename = f"fallback_report_{int(time.time())}.html"
352
+ reports_dir = os.path.join(self.workspace_dir, "reports")
353
+ os.makedirs(reports_dir, exist_ok=True)
354
+ report_path = os.path.join(reports_dir, report_filename)
355
+
356
+ with open(report_path, 'w', encoding='utf-8') as f:
357
+ f.write(html_content)
358
+
359
+ logger.info(f"✅ Fallback report generated: {report_path}")
360
+ return report_path