vibesurf 0.1.9a6__py3-none-any.whl → 0.1.11__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.
- vibe_surf/_version.py +2 -2
- vibe_surf/agents/browser_use_agent.py +68 -45
- vibe_surf/agents/prompts/report_writer_prompt.py +73 -0
- vibe_surf/agents/prompts/vibe_surf_prompt.py +85 -172
- vibe_surf/agents/report_writer_agent.py +380 -226
- vibe_surf/agents/vibe_surf_agent.py +878 -814
- vibe_surf/agents/views.py +130 -0
- vibe_surf/backend/api/activity.py +3 -1
- vibe_surf/backend/api/browser.py +70 -0
- vibe_surf/backend/api/config.py +8 -5
- vibe_surf/backend/api/files.py +59 -50
- vibe_surf/backend/api/models.py +2 -2
- vibe_surf/backend/api/task.py +47 -13
- vibe_surf/backend/database/manager.py +24 -18
- vibe_surf/backend/database/queries.py +199 -192
- vibe_surf/backend/database/schemas.py +1 -1
- vibe_surf/backend/main.py +80 -3
- vibe_surf/backend/shared_state.py +30 -35
- vibe_surf/backend/utils/encryption.py +3 -1
- vibe_surf/backend/utils/llm_factory.py +41 -36
- vibe_surf/browser/agent_browser_session.py +308 -62
- vibe_surf/browser/browser_manager.py +71 -100
- vibe_surf/browser/utils.py +5 -3
- vibe_surf/browser/watchdogs/dom_watchdog.py +0 -45
- vibe_surf/chrome_extension/background.js +88 -0
- vibe_surf/chrome_extension/manifest.json +3 -1
- vibe_surf/chrome_extension/scripts/api-client.js +13 -0
- vibe_surf/chrome_extension/scripts/file-manager.js +482 -0
- vibe_surf/chrome_extension/scripts/history-manager.js +658 -0
- vibe_surf/chrome_extension/scripts/modal-manager.js +487 -0
- vibe_surf/chrome_extension/scripts/session-manager.js +52 -11
- vibe_surf/chrome_extension/scripts/settings-manager.js +1214 -0
- vibe_surf/chrome_extension/scripts/ui-manager.js +1530 -3163
- vibe_surf/chrome_extension/sidepanel.html +47 -7
- vibe_surf/chrome_extension/styles/activity.css +934 -0
- vibe_surf/chrome_extension/styles/base.css +76 -0
- vibe_surf/chrome_extension/styles/history-modal.css +791 -0
- vibe_surf/chrome_extension/styles/input.css +568 -0
- vibe_surf/chrome_extension/styles/layout.css +186 -0
- vibe_surf/chrome_extension/styles/responsive.css +454 -0
- vibe_surf/chrome_extension/styles/settings-environment.css +165 -0
- vibe_surf/chrome_extension/styles/settings-forms.css +389 -0
- vibe_surf/chrome_extension/styles/settings-modal.css +141 -0
- vibe_surf/chrome_extension/styles/settings-profiles.css +244 -0
- vibe_surf/chrome_extension/styles/settings-responsive.css +144 -0
- vibe_surf/chrome_extension/styles/settings-utilities.css +25 -0
- vibe_surf/chrome_extension/styles/variables.css +54 -0
- vibe_surf/cli.py +5 -22
- vibe_surf/common.py +35 -0
- vibe_surf/llm/openai_compatible.py +148 -93
- vibe_surf/logger.py +99 -0
- vibe_surf/{controller/vibesurf_tools.py → tools/browser_use_tools.py} +233 -221
- vibe_surf/tools/file_system.py +415 -0
- vibe_surf/{controller → tools}/mcp_client.py +4 -3
- vibe_surf/tools/report_writer_tools.py +21 -0
- vibe_surf/tools/vibesurf_tools.py +657 -0
- vibe_surf/tools/views.py +120 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/METADATA +23 -3
- vibesurf-0.1.11.dist-info/RECORD +93 -0
- vibe_surf/chrome_extension/styles/main.css +0 -2338
- vibe_surf/chrome_extension/styles/settings.css +0 -1100
- vibe_surf/controller/file_system.py +0 -53
- vibe_surf/controller/views.py +0 -37
- vibesurf-0.1.9a6.dist-info/RECORD +0 -71
- /vibe_surf/{controller → tools}/__init__.py +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/top_level.txt +0 -0
|
@@ -10,6 +10,8 @@ from dataclasses import dataclass, field
|
|
|
10
10
|
from datetime import datetime
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
import pickle
|
|
13
|
+
import nanoid
|
|
14
|
+
import shutil
|
|
13
15
|
from typing import Any, Dict, List, Literal, Optional
|
|
14
16
|
from uuid_extensions import uuid7str
|
|
15
17
|
from collections import defaultdict
|
|
@@ -22,50 +24,35 @@ from browser_use.browser.session import BrowserSession
|
|
|
22
24
|
from browser_use.llm.base import BaseChatModel
|
|
23
25
|
from browser_use.llm.messages import UserMessage, SystemMessage, BaseMessage, AssistantMessage
|
|
24
26
|
from browser_use.browser.views import TabInfo
|
|
27
|
+
from browser_use.tokens.service import TokenCost
|
|
25
28
|
|
|
26
29
|
from vibe_surf.agents.browser_use_agent import BrowserUseAgent
|
|
27
|
-
from vibe_surf.agents.report_writer_agent import ReportWriterAgent
|
|
30
|
+
from vibe_surf.agents.report_writer_agent import ReportWriterAgent, ReportTaskResult
|
|
31
|
+
from vibe_surf.agents.views import CustomAgentOutput
|
|
28
32
|
|
|
29
33
|
from vibe_surf.agents.prompts.vibe_surf_prompt import (
|
|
30
|
-
|
|
34
|
+
VIBESURF_SYSTEM_PROMPT,
|
|
35
|
+
EXTEND_BU_SYSTEM_PROMPT
|
|
31
36
|
)
|
|
32
37
|
from vibe_surf.browser.browser_manager import BrowserManager
|
|
33
|
-
from vibe_surf.
|
|
38
|
+
from vibe_surf.tools.browser_use_tools import BrowserUseTools
|
|
39
|
+
from vibe_surf.tools.vibesurf_tools import VibeSurfTools
|
|
40
|
+
from vibe_surf.tools.file_system import CustomFileSystem
|
|
34
41
|
|
|
35
|
-
logger
|
|
42
|
+
from vibe_surf.logger import get_logger
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
class TodoItem(BaseModel):
|
|
39
|
-
"""Individual todo item with simple string-based task description"""
|
|
40
|
-
task: str = Field(description="Simple task description")
|
|
41
|
-
status: Literal["pending", "in_progress", "completed"] = "pending"
|
|
42
|
-
assigned_agent_id: Optional[str] = None
|
|
43
|
-
result: Optional[str] = None
|
|
44
|
-
error: Optional[str] = None
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class ExecutionMode(BaseModel):
|
|
48
|
-
"""Execution mode configuration"""
|
|
49
|
-
mode: Literal["single", "parallel"] = "single"
|
|
50
|
-
reason: str = Field(description="LLM reasoning for mode selection")
|
|
44
|
+
logger = get_logger(__name__)
|
|
51
45
|
|
|
52
46
|
|
|
53
47
|
class BrowserTaskResult(BaseModel):
|
|
54
48
|
"""Result from browser task execution"""
|
|
55
49
|
agent_id: str
|
|
56
|
-
|
|
50
|
+
agent_workdir: str
|
|
57
51
|
success: bool
|
|
52
|
+
task: Optional[str] = None
|
|
58
53
|
result: Optional[str] = None
|
|
59
54
|
error: Optional[str] = None
|
|
60
|
-
|
|
61
|
-
extracted_data: Optional[str] = None
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class ReportRequirement(BaseModel):
|
|
65
|
-
"""Indicates if and what type of report is needed"""
|
|
66
|
-
needs_report: bool = False
|
|
67
|
-
report_type: Literal["summary", "detailed", "none"] = "none"
|
|
68
|
-
reason: str = Field(description="LLM reasoning for report decision")
|
|
55
|
+
important_files: Optional[List[str]] = None
|
|
69
56
|
|
|
70
57
|
|
|
71
58
|
class ControlResult(BaseModel):
|
|
@@ -97,50 +84,33 @@ class VibeSurfStatus(BaseModel):
|
|
|
97
84
|
|
|
98
85
|
@dataclass
|
|
99
86
|
class VibeSurfState:
|
|
100
|
-
"""
|
|
87
|
+
"""LangGraph state for VibeSurfAgent workflow"""
|
|
101
88
|
|
|
102
89
|
# Core task information
|
|
103
90
|
original_task: str = ""
|
|
104
91
|
upload_files: List[str] = field(default_factory=list)
|
|
105
92
|
session_id: str = field(default_factory=lambda: uuid7str())
|
|
106
|
-
|
|
93
|
+
current_workspace_dir: str = "./workspace"
|
|
107
94
|
|
|
108
95
|
# Workflow state
|
|
109
|
-
current_step: str = "
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# Supervisor Agent - Core controller with message history
|
|
113
|
-
supervisor_message_history: List[BaseMessage] = field(default_factory=list)
|
|
114
|
-
supervisor_action: Optional[str] = None
|
|
96
|
+
current_step: str = "vibesurf_agent"
|
|
97
|
+
is_complete: bool = False
|
|
115
98
|
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
current_task_index: int = 0
|
|
99
|
+
# Current action and parameters from LLM
|
|
100
|
+
current_action: Optional[str] = None
|
|
101
|
+
action_params: Optional[Dict[str, Any]] = None
|
|
120
102
|
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
pending_todo_indices: List[int] = field(default_factory=list) # Track which todo indices are being executed
|
|
125
|
-
browser_results: List[BrowserTaskResult] = field(default_factory=list) # record all browser result
|
|
126
|
-
prev_browser_results: List[BrowserTaskResult] = field(default_factory=list) # record previous browser result
|
|
103
|
+
# Browser task execution
|
|
104
|
+
browser_tasks: List[Dict[str, Any]] = field(default_factory=list)
|
|
105
|
+
browser_results: List[BrowserTaskResult] = field(default_factory=list)
|
|
127
106
|
|
|
107
|
+
generated_report_result: Optional[ReportTaskResult] = None
|
|
108
|
+
|
|
128
109
|
# Response outputs
|
|
129
|
-
|
|
130
|
-
generated_report_path: Optional[str] = None
|
|
131
|
-
final_summary: Optional[str] = None
|
|
132
|
-
is_complete: bool = False
|
|
133
|
-
|
|
134
|
-
# File organization
|
|
135
|
-
workspace_dir: str = "./workspace"
|
|
136
|
-
session_dir: Optional[str] = None
|
|
137
|
-
task_dir: Optional[str] = None
|
|
110
|
+
final_response: Optional[str] = None
|
|
138
111
|
|
|
139
|
-
#
|
|
140
|
-
|
|
141
|
-
vibesurf_controller: Optional[VibeSurfController] = None
|
|
142
|
-
llm: Optional[BaseChatModel] = None
|
|
143
|
-
vibesurf_agent: Optional[Any] = None
|
|
112
|
+
# vibesurf_agent
|
|
113
|
+
vibesurf_agent: Optional['VibeSurfAgent'] = None
|
|
144
114
|
|
|
145
115
|
# Control state management
|
|
146
116
|
paused: bool = False
|
|
@@ -148,139 +118,6 @@ class VibeSurfState:
|
|
|
148
118
|
should_pause: bool = False
|
|
149
119
|
should_stop: bool = False
|
|
150
120
|
|
|
151
|
-
# Agent control tracking
|
|
152
|
-
agent_control_states: Dict[str, Dict[str, Any]] = field(default_factory=dict)
|
|
153
|
-
paused_agents: set = field(default_factory=set)
|
|
154
|
-
|
|
155
|
-
# Control metadata
|
|
156
|
-
control_timestamps: Dict[str, datetime] = field(default_factory=dict)
|
|
157
|
-
control_reasons: Dict[str, str] = field(default_factory=dict)
|
|
158
|
-
last_control_action: Optional[str] = None
|
|
159
|
-
|
|
160
|
-
# Agent activity log
|
|
161
|
-
agent_activity_logs: List[Dict[str, str]] = field(default_factory=list)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
# Utility functions for parsing LLM JSON responses
|
|
165
|
-
def parse_json_response(response_text: str, fallback_data: Dict) -> Dict:
|
|
166
|
-
"""Parse JSON response with repair capability"""
|
|
167
|
-
try:
|
|
168
|
-
# Try to find JSON in the response
|
|
169
|
-
json_start = response_text.find('{')
|
|
170
|
-
json_end = response_text.rfind('}') + 1
|
|
171
|
-
|
|
172
|
-
if json_start >= 0 and json_end > json_start:
|
|
173
|
-
json_text = response_text[json_start:json_end]
|
|
174
|
-
try:
|
|
175
|
-
return json.loads(json_text)
|
|
176
|
-
except json.JSONDecodeError:
|
|
177
|
-
# Try to repair JSON
|
|
178
|
-
repaired_json = repair_json(json_text)
|
|
179
|
-
return json.loads(repaired_json)
|
|
180
|
-
|
|
181
|
-
# If no JSON found, return fallback
|
|
182
|
-
return fallback_data
|
|
183
|
-
except Exception as e:
|
|
184
|
-
logger.warning(f"JSON parsing failed: {e}, using fallback")
|
|
185
|
-
return fallback_data
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def parse_task_analysis_response(response_text: str) -> Dict:
|
|
189
|
-
"""Parse task analysis JSON response for simple response detection"""
|
|
190
|
-
fallback = {
|
|
191
|
-
"is_simple_response": False,
|
|
192
|
-
"reasoning": "Failed to parse response, defaulting to complex task",
|
|
193
|
-
"simple_response_content": None
|
|
194
|
-
}
|
|
195
|
-
return parse_json_response(response_text, fallback)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def parse_supervisor_response(response_text: str) -> Dict:
|
|
199
|
-
"""Parse supervisor agent JSON response"""
|
|
200
|
-
fallback = {
|
|
201
|
-
"action": "summary_generation",
|
|
202
|
-
"reasoning": "Failed to parse response, defaulting to summary generation",
|
|
203
|
-
"todo_items": [],
|
|
204
|
-
"task_type": "single",
|
|
205
|
-
"tasks_to_execute": [],
|
|
206
|
-
"summary_content": None
|
|
207
|
-
}
|
|
208
|
-
result = parse_json_response(response_text, fallback)
|
|
209
|
-
|
|
210
|
-
# Ensure todo_items is always a list for actions that modify todos
|
|
211
|
-
if result.get("action") in ["generate_todos", "update_todos"]:
|
|
212
|
-
if "todo_items" not in result or not isinstance(result["todo_items"], list):
|
|
213
|
-
result["todo_items"] = []
|
|
214
|
-
|
|
215
|
-
return result
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def parse_todo_generation_response(response_text: str) -> List[str]:
|
|
219
|
-
"""Parse todo generation JSON response"""
|
|
220
|
-
fallback = {
|
|
221
|
-
"todo_items": ["Complete the original task"]
|
|
222
|
-
}
|
|
223
|
-
result = parse_json_response(response_text, fallback)
|
|
224
|
-
return result.get("todo_items", fallback["todo_items"])[:5] # Max 5 items
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def parse_execution_planning_response(response_text: str) -> ExecutionMode:
|
|
228
|
-
"""Parse execution planning JSON response"""
|
|
229
|
-
fallback = {
|
|
230
|
-
"execution_mode": "single",
|
|
231
|
-
"reasoning": "Default single mode"
|
|
232
|
-
}
|
|
233
|
-
result = parse_json_response(response_text, fallback)
|
|
234
|
-
|
|
235
|
-
return ExecutionMode(
|
|
236
|
-
mode=result.get("execution_mode", "single"),
|
|
237
|
-
reason=result.get("reasoning", "Default execution mode")
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def parse_todo_update_response(response_text: str) -> Dict:
|
|
242
|
-
"""Parse todo update JSON response"""
|
|
243
|
-
fallback = {
|
|
244
|
-
"additional_tasks": [],
|
|
245
|
-
"next_action": "summary_generation",
|
|
246
|
-
"reasoning": "Default action"
|
|
247
|
-
}
|
|
248
|
-
return parse_json_response(response_text, fallback)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def parse_report_decision_response(response_text: str) -> ReportRequirement:
|
|
252
|
-
"""Parse report decision JSON response"""
|
|
253
|
-
fallback = {
|
|
254
|
-
"needs_report": False,
|
|
255
|
-
"report_type": "none",
|
|
256
|
-
"reasoning": "Default no report"
|
|
257
|
-
}
|
|
258
|
-
result = parse_json_response(response_text, fallback)
|
|
259
|
-
|
|
260
|
-
return ReportRequirement(
|
|
261
|
-
needs_report=result.get("needs_report", False),
|
|
262
|
-
report_type=result.get("report_type", "none"),
|
|
263
|
-
reason=result.get("reasoning", "Default report decision")
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def format_upload_files_list(upload_files: Optional[List[str]] = None) -> str:
|
|
268
|
-
"""Format uploaded file list for LLM prompt"""
|
|
269
|
-
if upload_files is None:
|
|
270
|
-
return ""
|
|
271
|
-
return "\n".join(
|
|
272
|
-
[f"{i + 1}. [{os.path.basename(file_path)}](file:///{file_path})" for i, file_path in enumerate(upload_files)])
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
def format_todo_list(todo_list: List[TodoItem]) -> str:
|
|
276
|
-
"""Format todo list for LLM prompt"""
|
|
277
|
-
return "\n".join([f"{i + 1}. {item.task}" for i, item in enumerate(todo_list)])
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
def format_completed_todos(completed_todos: List[TodoItem]) -> str:
|
|
281
|
-
"""Format completed todos for LLM prompt"""
|
|
282
|
-
return "\n".join([f"✅ {item.task} - {item.result or 'Completed'}" for item in completed_todos])
|
|
283
|
-
|
|
284
121
|
|
|
285
122
|
def format_browser_results(browser_results: List[BrowserTaskResult]) -> str:
|
|
286
123
|
"""Format browser results for LLM prompt"""
|
|
@@ -295,97 +132,163 @@ def format_browser_results(browser_results: List[BrowserTaskResult]) -> str:
|
|
|
295
132
|
return "\n".join(result_text)
|
|
296
133
|
|
|
297
134
|
|
|
298
|
-
def
|
|
299
|
-
"""
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
135
|
+
def process_agent_msg_file_links(agent_msg: str, agent_name: str, base_dir: Path) -> str:
|
|
136
|
+
"""
|
|
137
|
+
Process file links in agent_msg, converting relative paths to absolute paths
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
agent_msg: The agent message containing potential file links
|
|
141
|
+
agent_name: Name of the agent (used for special handling of browser_use_agent)
|
|
142
|
+
base_dir: Base directory path from file_system.get_dir()
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Processed agent_msg with absolute paths
|
|
146
|
+
"""
|
|
147
|
+
# Pattern to match markdown links: [text](path)
|
|
148
|
+
link_pattern = r'\[([^\]]*)\]\(([^)]+)\)'
|
|
149
|
+
|
|
150
|
+
def replace_link(match):
|
|
151
|
+
text = match.group(1)
|
|
152
|
+
path = match.group(2)
|
|
153
|
+
|
|
154
|
+
# Skip if already an absolute path or URL
|
|
155
|
+
if path.startswith(('http://', 'https://', 'file:///', '/')):
|
|
156
|
+
return match.group(0)
|
|
157
|
+
|
|
158
|
+
# Build absolute path
|
|
159
|
+
if agent_name.startswith('browser_use_agent-'):
|
|
160
|
+
# Extract task_id and index from agent_name
|
|
161
|
+
# Format: browser_use_agent-{task_id}-{i + 1:03d}
|
|
162
|
+
parts = agent_name.split('-')
|
|
163
|
+
if len(parts) >= 3:
|
|
164
|
+
task_id = parts[1]
|
|
165
|
+
index = parts[2]
|
|
166
|
+
# Add the special sub-path for browser_use_agent
|
|
167
|
+
sub_path = f"bu_agents/{task_id}-{index}"
|
|
168
|
+
absolute_path = base_dir / sub_path / path
|
|
169
|
+
else:
|
|
170
|
+
absolute_path = base_dir / path
|
|
307
171
|
else:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
172
|
+
absolute_path = base_dir / path
|
|
173
|
+
|
|
174
|
+
# Convert to string and normalize separators
|
|
175
|
+
abs_path_str = str(absolute_path).replace(os.path.sep, '/')
|
|
176
|
+
|
|
177
|
+
return f"[{text}](file:///{abs_path_str})"
|
|
178
|
+
|
|
179
|
+
# Replace all file links
|
|
180
|
+
processed_msg = re.sub(link_pattern, replace_link, agent_msg)
|
|
181
|
+
return processed_msg
|
|
312
182
|
|
|
313
183
|
|
|
314
|
-
def log_agent_activity(state: VibeSurfState, agent_name: str, agent_status: str, agent_msg: str) -> None:
|
|
184
|
+
async def log_agent_activity(state: VibeSurfState, agent_name: str, agent_status: str, agent_msg: str) -> None:
|
|
315
185
|
"""Log agent activity to the activity log"""
|
|
186
|
+
token_summary = await state.vibesurf_agent.token_cost_service.get_usage_summary()
|
|
187
|
+
token_summary_md = token_summary.model_dump_json(indent=2, exclude_none=True, exclude_unset=True)
|
|
188
|
+
logger.debug(token_summary_md)
|
|
189
|
+
|
|
190
|
+
# Process file links in agent_msg to convert relative paths to absolute paths
|
|
191
|
+
base_dir = state.vibesurf_agent.file_system.get_dir()
|
|
192
|
+
processed_agent_msg = process_agent_msg_file_links(agent_msg, agent_name, base_dir)
|
|
193
|
+
|
|
316
194
|
activity_entry = {
|
|
317
195
|
"agent_name": agent_name,
|
|
318
196
|
"agent_status": agent_status, # working, result, error
|
|
319
|
-
"agent_msg":
|
|
197
|
+
"agent_msg": processed_agent_msg,
|
|
198
|
+
"timestamp": datetime.now().isoformat(),
|
|
199
|
+
"total_tokens": token_summary.total_tokens,
|
|
200
|
+
"total_cost": token_summary.total_cost
|
|
320
201
|
}
|
|
321
|
-
state.
|
|
322
|
-
logger.
|
|
202
|
+
state.vibesurf_agent.activity_logs.append(activity_entry)
|
|
203
|
+
logger.debug(f"📝 Logged activity: {agent_name} - {agent_status}:\n{processed_agent_msg}")
|
|
323
204
|
|
|
324
205
|
|
|
325
206
|
def create_browser_agent_step_callback(state: VibeSurfState, agent_name: str):
|
|
326
207
|
"""Create a step callback function for browser-use agent to log each step"""
|
|
327
|
-
|
|
328
|
-
def step_callback(browser_state_summary, agent_output, step_num: int) -> None:
|
|
208
|
+
|
|
209
|
+
async def step_callback(browser_state_summary, agent_output, step_num: int) -> None:
|
|
329
210
|
"""Callback function to log browser agent step information"""
|
|
330
211
|
try:
|
|
331
212
|
# Format step information as markdown
|
|
332
213
|
step_msg = f"## Step {step_num}\n\n"
|
|
333
|
-
|
|
214
|
+
|
|
334
215
|
# Add thinking if present
|
|
335
216
|
if agent_output.thinking:
|
|
336
217
|
step_msg += f"**💡 Thinking:**\n{agent_output.thinking}\n\n"
|
|
337
|
-
|
|
218
|
+
|
|
338
219
|
# Add evaluation if present
|
|
339
220
|
if agent_output.evaluation_previous_goal:
|
|
340
221
|
step_msg += f"**👍 Evaluation:**\n{agent_output.evaluation_previous_goal}\n\n"
|
|
341
|
-
|
|
222
|
+
|
|
342
223
|
# Add memory if present
|
|
343
224
|
# if agent_output.memory:
|
|
344
225
|
# step_msg += f"**🧠 Memory:** {agent_output.memory}\n\n"
|
|
345
|
-
|
|
226
|
+
|
|
346
227
|
# Add next goal if present
|
|
347
228
|
if agent_output.next_goal:
|
|
348
229
|
step_msg += f"**🎯 Next Goal:**\n{agent_output.next_goal}\n\n"
|
|
349
|
-
|
|
230
|
+
|
|
350
231
|
# Add action summary
|
|
351
232
|
if agent_output.action and len(agent_output.action) > 0:
|
|
352
233
|
action_count = len(agent_output.action)
|
|
353
234
|
step_msg += f"**⚡ Actions:**\n"
|
|
354
|
-
|
|
235
|
+
|
|
236
|
+
all_action_data = []
|
|
355
237
|
# Add brief action details
|
|
356
|
-
for i, action in enumerate(agent_output.action):
|
|
357
|
-
action_data = action.model_dump(exclude_unset=True)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
step_msg += f"- [x] {action_name}: {action_params}\n"
|
|
238
|
+
for i, action in enumerate(agent_output.action):
|
|
239
|
+
action_data = action.model_dump(exclude_unset=True, exclude_none=True)
|
|
240
|
+
all_action_data.append(action_data)
|
|
241
|
+
step_msg += f"```json\n{json.dumps(all_action_data, indent=2, ensure_ascii=False)}\n```"
|
|
361
242
|
else:
|
|
362
243
|
step_msg += f"**⚡ Actions:** No actions\n"
|
|
363
|
-
|
|
244
|
+
|
|
364
245
|
# Log the step activity
|
|
365
|
-
log_agent_activity(state, agent_name, "working", step_msg.strip())
|
|
366
|
-
|
|
246
|
+
await log_agent_activity(state, agent_name, "working", step_msg.strip())
|
|
247
|
+
|
|
367
248
|
except Exception as e:
|
|
368
249
|
logger.error(f"❌ Error in step callback for {agent_name}: {e}")
|
|
369
250
|
# Log a simple fallback message
|
|
370
|
-
log_agent_activity(state, agent_name, "step", f"Step {step_num} completed")
|
|
371
|
-
|
|
251
|
+
await log_agent_activity(state, agent_name, "step", f"Step {step_num} completed")
|
|
252
|
+
|
|
372
253
|
return step_callback
|
|
373
254
|
|
|
374
255
|
|
|
375
|
-
def
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
256
|
+
def create_report_writer_step_callback(state: VibeSurfState, agent_name: str):
|
|
257
|
+
"""Create a step callback function for report writer agent to log each step"""
|
|
258
|
+
|
|
259
|
+
async def step_callback(parsed_output, step_num: int) -> None:
|
|
260
|
+
"""Callback function to log report writer agent step information"""
|
|
261
|
+
try:
|
|
262
|
+
# Format step information as markdown
|
|
263
|
+
step_msg = f"## Step {step_num}\n\n"
|
|
264
|
+
|
|
265
|
+
# Add thinking if present
|
|
266
|
+
if hasattr(parsed_output, 'thinking') and parsed_output.thinking:
|
|
267
|
+
step_msg += f"**💡 Thinking:**\n{parsed_output.thinking}\n\n"
|
|
268
|
+
|
|
269
|
+
# Add action summary
|
|
270
|
+
if hasattr(parsed_output, 'action') and parsed_output.action and len(parsed_output.action) > 0:
|
|
271
|
+
action_count = len(parsed_output.action)
|
|
272
|
+
step_msg += f"**⚡ Actions:**\n"
|
|
273
|
+
|
|
274
|
+
# Add brief action details
|
|
275
|
+
all_action_data = []
|
|
276
|
+
for i, action in enumerate(parsed_output.action):
|
|
277
|
+
action_data = action.model_dump(exclude_unset=True, exclude_none=True)
|
|
278
|
+
all_action_data.append(action_data)
|
|
279
|
+
step_msg += f"```json\n{json.dumps(all_action_data, indent=2, ensure_ascii=False)}\n```"
|
|
280
|
+
else:
|
|
281
|
+
step_msg += f"**⚡ Actions:** No actions\n"
|
|
282
|
+
|
|
283
|
+
# Log the step activity
|
|
284
|
+
await log_agent_activity(state, agent_name, "working", step_msg.strip())
|
|
380
285
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
286
|
+
except Exception as e:
|
|
287
|
+
logger.error(f"❌ Error in step callback for {agent_name}: {e}")
|
|
288
|
+
# Log a simple fallback message
|
|
289
|
+
await log_agent_activity(state, agent_name, "step", f"Step {step_num} completed")
|
|
384
290
|
|
|
385
|
-
|
|
386
|
-
os.makedirs(os.path.join(state.task_dir, "screenshots"), exist_ok=True)
|
|
387
|
-
os.makedirs(os.path.join(state.task_dir, "reports"), exist_ok=True)
|
|
388
|
-
os.makedirs(os.path.join(state.task_dir, "logs"), exist_ok=True)
|
|
291
|
+
return step_callback
|
|
389
292
|
|
|
390
293
|
|
|
391
294
|
# Control-aware node wrapper
|
|
@@ -404,7 +307,6 @@ async def control_aware_node(node_func, state: VibeSurfState, node_name: str) ->
|
|
|
404
307
|
logger.info(f"⏸️ Node {node_name} pausing workflow")
|
|
405
308
|
state.paused = True
|
|
406
309
|
state.should_pause = False
|
|
407
|
-
state.control_timestamps["paused"] = datetime.now()
|
|
408
310
|
|
|
409
311
|
logger.debug(f"⏸️ Node {node_name} waiting - workflow paused")
|
|
410
312
|
await asyncio.sleep(0.5) # Check every 500ms
|
|
@@ -414,7 +316,6 @@ async def control_aware_node(node_func, state: VibeSurfState, node_name: str) ->
|
|
|
414
316
|
logger.info(f"🛑 Node {node_name} stopped while paused")
|
|
415
317
|
state.stopped = True
|
|
416
318
|
state.should_stop = False
|
|
417
|
-
state.control_timestamps["stopped"] = datetime.now()
|
|
418
319
|
return state
|
|
419
320
|
|
|
420
321
|
# Check for stop signal
|
|
@@ -422,12 +323,10 @@ async def control_aware_node(node_func, state: VibeSurfState, node_name: str) ->
|
|
|
422
323
|
logger.info(f"🛑 Node {node_name} stopping workflow")
|
|
423
324
|
state.stopped = True
|
|
424
325
|
state.should_stop = False
|
|
425
|
-
state.control_timestamps["stopped"] = datetime.now()
|
|
426
326
|
return state
|
|
427
327
|
|
|
428
328
|
# Execute the actual node
|
|
429
329
|
logger.debug(f"▶️ Executing node: {node_name}")
|
|
430
|
-
state.last_control_action = f"executing_{node_name}"
|
|
431
330
|
|
|
432
331
|
try:
|
|
433
332
|
return await node_func(state)
|
|
@@ -438,174 +337,163 @@ async def control_aware_node(node_func, state: VibeSurfState, node_name: str) ->
|
|
|
438
337
|
|
|
439
338
|
# LangGraph Nodes
|
|
440
339
|
|
|
441
|
-
async def
|
|
340
|
+
async def vibesurf_agent_node(state: VibeSurfState) -> VibeSurfState:
|
|
442
341
|
"""
|
|
443
|
-
|
|
342
|
+
Main VibeSurf agent node using thinking + action pattern like report_writer_agent
|
|
444
343
|
"""
|
|
445
|
-
return await control_aware_node(
|
|
446
|
-
|
|
344
|
+
return await control_aware_node(_vibesurf_agent_node_impl, state, "vibesurf_agent")
|
|
447
345
|
|
|
448
|
-
def format_browser_tabs(tabs: Optional[List[TabInfo]] = None) -> str:
|
|
449
|
-
if not tabs:
|
|
450
|
-
return ""
|
|
451
|
-
return "\n".join([f"[{i}] Page Title: {item.title}, Page Url: {item.url}, Page ID: {item.target_id}" for i, item in enumerate(tabs)])
|
|
452
346
|
|
|
347
|
+
async def _vibesurf_agent_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
348
|
+
"""Implementation using thinking + action pattern similar to report_writer_agent"""
|
|
453
349
|
|
|
454
|
-
|
|
455
|
-
"""Implementation of supervisor agent node - core workflow controller"""
|
|
456
|
-
logger.info("🎯 Supervisor Agent: Managing workflow and task coordination...")
|
|
350
|
+
agent_name = "vibesurf_agent"
|
|
457
351
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
if
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
352
|
+
# Create action model and agent output using VibeSurfTools
|
|
353
|
+
vibesurf_agent = state.vibesurf_agent
|
|
354
|
+
ActionModel = vibesurf_agent.tools.registry.create_action_model()
|
|
355
|
+
if vibesurf_agent.thinking_mode:
|
|
356
|
+
AgentOutput = CustomAgentOutput.type_with_custom_actions(ActionModel)
|
|
357
|
+
else:
|
|
358
|
+
AgentOutput = CustomAgentOutput.type_with_custom_actions_no_thinking(ActionModel)
|
|
359
|
+
|
|
360
|
+
# Get current browser context
|
|
361
|
+
browser_tabs = await vibesurf_agent.browser_manager.main_browser_session.get_tabs()
|
|
362
|
+
active_browser_tab = await vibesurf_agent.browser_manager.get_activate_tab()
|
|
363
|
+
|
|
364
|
+
# Format context information
|
|
365
|
+
context_info = []
|
|
366
|
+
if browser_tabs:
|
|
367
|
+
browser_tabs_info = {}
|
|
368
|
+
for tab in browser_tabs:
|
|
369
|
+
browser_tabs_info[tab.target_id[-4:]] = {
|
|
370
|
+
"page_title": tab.title,
|
|
371
|
+
"page_url": tab.url,
|
|
372
|
+
}
|
|
373
|
+
context_info.append(
|
|
374
|
+
f"Current Available Browser Tabs:\n{json.dumps(browser_tabs_info, ensure_ascii=False, indent=2)}\n")
|
|
375
|
+
if active_browser_tab:
|
|
376
|
+
context_info.append(f"Current Active Browser Tab:{active_browser_tab.target_id[-4:]}\n")
|
|
377
|
+
if state.browser_results:
|
|
378
|
+
results_md = format_browser_results(state.browser_results)
|
|
379
|
+
context_info.append(f"Previous Browser Results:\n{results_md}\n")
|
|
380
|
+
if state.generated_report_result:
|
|
381
|
+
if state.generated_report_result.success:
|
|
382
|
+
context_info.append(f"Generated Report: ✅ Success - {state.generated_report_result.report_path}\n")
|
|
383
|
+
else:
|
|
384
|
+
context_info.append(f"Generated Report: ❌ Failed - {state.generated_report_result.msg}\nPath: {state.generated_report_result.report_path}\n")
|
|
471
385
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
if browser_tabs_md:
|
|
475
|
-
supervisor_message_history.append(UserMessage(
|
|
476
|
-
content=f"Available Browser Tabs:\n{browser_tabs_md}"))
|
|
386
|
+
context_str = "\n".join(context_info) if context_info else "No additional context available."
|
|
387
|
+
vibesurf_agent.message_history.append(UserMessage(content=context_str))
|
|
477
388
|
|
|
478
|
-
# Reset prev_browser_results
|
|
479
|
-
state.prev_browser_results = []
|
|
480
389
|
try:
|
|
481
|
-
response
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
# Log
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
#
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
log_agent_activity(state, "supervisor_agent", "result", f"Todo List:\n\n{todo_todo_list_md}")
|
|
506
|
-
# Continue in supervisor to assign tasks
|
|
507
|
-
state.current_step = "supervisor_agent"
|
|
508
|
-
|
|
509
|
-
elif action == "update_todos":
|
|
510
|
-
# Replace all remaining todos with the new list
|
|
511
|
-
todo_items = supervisor_result.get("todo_items", [])
|
|
512
|
-
if todo_items:
|
|
513
|
-
# Clear current todo list and replace with new items
|
|
514
|
-
state.todo_list = [TodoItem(task=task) for task in todo_items]
|
|
515
|
-
todo_todo_list_md = format_todo_list_markdown(state.completed_todos + state.todo_list)
|
|
516
|
-
supervisor_message_history.append(
|
|
517
|
-
UserMessage(content=f"Successfully Updated todo list:\n{todo_todo_list_md}"))
|
|
518
|
-
log_agent_activity(state, "supervisor_agent", "result", f"Todo List:\n\n{todo_todo_list_md}")
|
|
519
|
-
else:
|
|
520
|
-
# If no todo_items provided, clear the list
|
|
521
|
-
state.todo_list = []
|
|
522
|
-
todo_todo_list_md = format_todo_list_markdown(state.completed_todos + state.todo_list)
|
|
523
|
-
supervisor_message_history.append(
|
|
524
|
-
UserMessage(content=f"Cleared todo list - all tasks completed:\n{todo_todo_list_md}"))
|
|
525
|
-
log_agent_activity(state, "supervisor_agent", "result",
|
|
526
|
-
f"Cleared todo list - all tasks completed\n{todo_todo_list_md}")
|
|
527
|
-
|
|
528
|
-
# Continue in supervisor to assign tasks
|
|
529
|
-
state.current_step = "supervisor_agent"
|
|
530
|
-
|
|
531
|
-
elif action == "assign_browser_task":
|
|
532
|
-
# Assign browser tasks
|
|
533
|
-
task_type = supervisor_result.get("task_type", "single")
|
|
534
|
-
tasks_to_execute = supervisor_result.get("tasks_to_execute", [])
|
|
535
|
-
|
|
536
|
-
if tasks_to_execute:
|
|
537
|
-
tasks_to_execute_new = []
|
|
538
|
-
todo_indices = [] # Track which todo items are being executed
|
|
539
|
-
|
|
540
|
-
for task_item in tasks_to_execute:
|
|
541
|
-
if isinstance(task_item, list):
|
|
542
|
-
# Format: [page_index, todo_index]
|
|
543
|
-
page_index, todo_index = task_item
|
|
544
|
-
if todo_index < len(state.todo_list):
|
|
545
|
-
task_description = state.todo_list[todo_index].task
|
|
546
|
-
tasks_to_execute_new.append([browser_tabs[page_index].target_id, task_description])
|
|
547
|
-
todo_indices.append(todo_index)
|
|
548
|
-
else:
|
|
549
|
-
# Format: todo_index
|
|
550
|
-
todo_index = task_item
|
|
551
|
-
if todo_index < len(state.todo_list):
|
|
552
|
-
task_description = state.todo_list[todo_index].task
|
|
553
|
-
tasks_to_execute_new.append(task_description)
|
|
554
|
-
todo_indices.append(todo_index)
|
|
555
|
-
|
|
556
|
-
state.execution_mode = ExecutionMode(
|
|
557
|
-
mode=task_type,
|
|
558
|
-
reason=reasoning
|
|
559
|
-
)
|
|
560
|
-
state.pending_tasks = tasks_to_execute_new
|
|
561
|
-
state.pending_todo_indices = todo_indices # Store which todo indices are being executed
|
|
390
|
+
# Get LLM response with action output format
|
|
391
|
+
response = await vibesurf_agent.llm.ainvoke(vibesurf_agent.message_history, output_format=AgentOutput)
|
|
392
|
+
parsed = response.completion
|
|
393
|
+
actions = parsed.action
|
|
394
|
+
vibesurf_agent.message_history.append(
|
|
395
|
+
AssistantMessage(content=json.dumps(response.completion.model_dump(exclude_none=True, exclude_unset=True),
|
|
396
|
+
ensure_ascii=False)))
|
|
397
|
+
|
|
398
|
+
# Log thinking if present
|
|
399
|
+
if hasattr(parsed, 'thinking') and parsed.thinking:
|
|
400
|
+
await log_agent_activity(state, agent_name, "thinking", parsed.thinking)
|
|
401
|
+
|
|
402
|
+
for i, action in enumerate(actions):
|
|
403
|
+
action_data = action.model_dump(exclude_unset=True)
|
|
404
|
+
action_name = next(iter(action_data.keys())) if action_data else 'unknown'
|
|
405
|
+
logger.info(f"🛠️ Processing VibeSurf action {i + 1}/{len(actions)}: {action_name}")
|
|
406
|
+
|
|
407
|
+
# Check for special routing actions
|
|
408
|
+
if action_name == 'execute_browser_use_agent':
|
|
409
|
+
# Route to browser task execution node
|
|
410
|
+
params = action_data[action_name]
|
|
411
|
+
state.browser_tasks = params.get('tasks', [])
|
|
412
|
+
state.current_action = 'execute_browser_use_agent'
|
|
413
|
+
state.action_params = params
|
|
562
414
|
state.current_step = "browser_task_execution"
|
|
563
415
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
#
|
|
589
|
-
|
|
416
|
+
# Log agent activity
|
|
417
|
+
browser_tasks_md = []
|
|
418
|
+
for browser_task in state.browser_tasks:
|
|
419
|
+
bu_task = browser_task.get('task', "")
|
|
420
|
+
if bu_task:
|
|
421
|
+
browser_tasks_md.append(f"- [ ] {bu_task}")
|
|
422
|
+
browser_tasks_md = '\n'.join(browser_tasks_md)
|
|
423
|
+
agent_msg = f"Routing to browser task execution with {len(state.browser_tasks)} browser tasks:\n\n{browser_tasks_md}"
|
|
424
|
+
await log_agent_activity(state, agent_name, "working", agent_msg)
|
|
425
|
+
logger.debug(agent_msg)
|
|
426
|
+
return state
|
|
427
|
+
|
|
428
|
+
elif action_name == 'execute_report_writer_agent':
|
|
429
|
+
# Route to report task execution node
|
|
430
|
+
params = action_data[action_name]
|
|
431
|
+
state.current_action = 'execute_report_writer_agent'
|
|
432
|
+
state.action_params = params
|
|
433
|
+
state.current_step = "report_task_execution"
|
|
434
|
+
report_task = params.get('task', "")
|
|
435
|
+
agent_msg = f"Routing to report generation with task:\n{report_task}"
|
|
436
|
+
await log_agent_activity(state, agent_name, "working", agent_msg)
|
|
437
|
+
return state
|
|
438
|
+
|
|
439
|
+
elif action_name == 'task_done':
|
|
440
|
+
# Handle response/completion - direct to END
|
|
441
|
+
params = action_data[action_name]
|
|
442
|
+
response_content = params.get('response', 'Task completed!')
|
|
443
|
+
follow_tasks = params.get('suggestion_follow_tasks', [])
|
|
444
|
+
state.current_step = "END"
|
|
445
|
+
|
|
446
|
+
# Format final response
|
|
447
|
+
final_response = f"{response_content}"
|
|
448
|
+
await log_agent_activity(state, agent_name, "result", final_response)
|
|
449
|
+
|
|
450
|
+
if follow_tasks:
|
|
451
|
+
await log_agent_activity(state, agent_name, "suggestion_tasks",
|
|
452
|
+
'\n'.join(follow_tasks))
|
|
453
|
+
final_response += "\n\n## Suggested Follow-up Tasks:\n"
|
|
454
|
+
for j, task in enumerate(follow_tasks[:3], 1):
|
|
455
|
+
final_response += f"{j}. {task}\n"
|
|
456
|
+
|
|
457
|
+
state.final_response = final_response
|
|
458
|
+
logger.debug(final_response)
|
|
590
459
|
state.is_complete = True
|
|
591
|
-
state
|
|
592
|
-
|
|
460
|
+
return state
|
|
461
|
+
|
|
593
462
|
else:
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
463
|
+
if "todos" in action_name:
|
|
464
|
+
todo_content = await vibesurf_agent.file_system.read_file('todo.md')
|
|
465
|
+
action_msg = f"{action_name}:\n\n{todo_content}"
|
|
466
|
+
logger.debug(action_msg)
|
|
467
|
+
await log_agent_activity(state, agent_name, "working", action_msg)
|
|
468
|
+
else:
|
|
469
|
+
action_msg = f"**⚡ Actions:**\n"
|
|
470
|
+
action_msg += f"```json\n{json.dumps(action_data, indent=2, ensure_ascii=False)}\n```"
|
|
471
|
+
logger.debug(action_msg)
|
|
472
|
+
await log_agent_activity(state, agent_name, "working", action_msg)
|
|
473
|
+
|
|
474
|
+
result = await vibesurf_agent.tools.act(
|
|
475
|
+
action=action,
|
|
476
|
+
browser_manager=vibesurf_agent.browser_manager,
|
|
477
|
+
llm=vibesurf_agent.llm,
|
|
478
|
+
file_system=vibesurf_agent.file_system,
|
|
479
|
+
)
|
|
480
|
+
state.current_step = "vibesurf_agent"
|
|
481
|
+
if result.extracted_content:
|
|
482
|
+
vibesurf_agent.message_history.append(
|
|
483
|
+
UserMessage(content=f'Action result:\n{result.extracted_content}'))
|
|
484
|
+
await log_agent_activity(state, agent_name, "result", result.extracted_content)
|
|
485
|
+
|
|
486
|
+
if result.error:
|
|
487
|
+
vibesurf_agent.message_history.append(UserMessage(content=f'Action error:\n{result.error}'))
|
|
488
|
+
await log_agent_activity(state, agent_name, "error", result.error)
|
|
602
489
|
|
|
603
490
|
return state
|
|
604
491
|
|
|
605
492
|
except Exception as e:
|
|
606
|
-
logger.error(f"❌
|
|
607
|
-
state.
|
|
608
|
-
|
|
493
|
+
logger.error(f"❌ VibeSurf agent failed: {e}")
|
|
494
|
+
state.final_response = f"Task execution failed: {str(e)}"
|
|
495
|
+
state.is_complete = True
|
|
496
|
+
await log_agent_activity(state, agent_name, "error", f"Agent failed: {str(e)}")
|
|
609
497
|
return state
|
|
610
498
|
|
|
611
499
|
|
|
@@ -617,187 +505,132 @@ async def browser_task_execution_node(state: VibeSurfState) -> VibeSurfState:
|
|
|
617
505
|
|
|
618
506
|
|
|
619
507
|
async def _browser_task_execution_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
620
|
-
"""Implementation of browser task execution node"""
|
|
621
|
-
logger.info("🚀 Executing browser tasks assigned by
|
|
622
|
-
|
|
623
|
-
# Log agent activity
|
|
624
|
-
log_agent_activity(state, "browser_task_executor", "working",
|
|
625
|
-
f"Executing {len(state.pending_tasks)} browser tasks in {state.execution_mode.mode if state.execution_mode else 'single'} mode")
|
|
626
|
-
|
|
627
|
-
# Setup file organization
|
|
628
|
-
ensure_directories(state)
|
|
629
|
-
|
|
508
|
+
"""Implementation of browser task execution node - simplified tab-based approach"""
|
|
509
|
+
logger.info("🚀 Executing browser tasks assigned by vibesurf agent...")
|
|
630
510
|
try:
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
511
|
+
task_count = len(state.browser_tasks)
|
|
512
|
+
if task_count == 0:
|
|
513
|
+
raise ValueError("No browser tasks assigned. Please assign 1 task at least.")
|
|
514
|
+
|
|
515
|
+
if task_count <= 1:
|
|
516
|
+
# Single task execution
|
|
517
|
+
logger.info("📝 Using single execution for single task")
|
|
518
|
+
result = await execute_single_browser_tasks(state)
|
|
519
|
+
results = [result]
|
|
520
|
+
# Update browser results
|
|
521
|
+
state.browser_results.extend(results)
|
|
634
522
|
else:
|
|
635
|
-
#
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
for i, todo_index in enumerate(state.pending_todo_indices):
|
|
644
|
-
if todo_index < len(state.todo_list) and state.todo_list[todo_index].status != "completed":
|
|
645
|
-
todo = state.todo_list[todo_index]
|
|
646
|
-
todo.status = "completed"
|
|
647
|
-
if i < len(results):
|
|
648
|
-
todo.result = results[i].result if results[i].success else None
|
|
649
|
-
todo.error = results[i].error if not results[i].success else None
|
|
650
|
-
state.completed_todos.append(todo)
|
|
651
|
-
|
|
652
|
-
# Remove completed todos from the todo list
|
|
653
|
-
# Sort indices in reverse order to avoid index shifting issues
|
|
654
|
-
for todo_index in sorted(state.pending_todo_indices, reverse=True):
|
|
655
|
-
if todo_index < len(state.todo_list):
|
|
656
|
-
state.todo_list.pop(todo_index)
|
|
657
|
-
|
|
658
|
-
# Clear pending tasks and indices
|
|
659
|
-
state.pending_tasks = []
|
|
660
|
-
state.pending_todo_indices = []
|
|
661
|
-
|
|
662
|
-
# Return to supervisor for next decision
|
|
663
|
-
state.current_step = "supervisor_agent"
|
|
523
|
+
# Multiple tasks execution - parallel approach
|
|
524
|
+
logger.info(f"🚀 Using parallel execution for {task_count} tasks")
|
|
525
|
+
results = await execute_parallel_browser_tasks(state)
|
|
526
|
+
# Update browser results
|
|
527
|
+
state.browser_results.extend(results)
|
|
528
|
+
|
|
529
|
+
# Return to vibesurf agent for next decision
|
|
530
|
+
state.current_step = "vibesurf_agent"
|
|
664
531
|
|
|
665
532
|
# Log result
|
|
666
533
|
successful_tasks = sum(1 for result in results if result.success)
|
|
667
|
-
log_agent_activity(state, "browser_task_executor", "result",
|
|
668
|
-
|
|
534
|
+
await log_agent_activity(state, "browser_task_executor", "result",
|
|
535
|
+
f"Browser execution completed: {successful_tasks}/{len(results)} tasks successful")
|
|
669
536
|
|
|
670
537
|
logger.info(f"✅ Browser task execution completed with {len(results)} results")
|
|
671
538
|
return state
|
|
672
539
|
|
|
673
540
|
except Exception as e:
|
|
674
541
|
logger.error(f"❌ Browser task execution failed: {e}")
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
task_description = task
|
|
684
|
-
error_results.append(BrowserTaskResult(
|
|
685
|
-
agent_id="error",
|
|
686
|
-
task=task_description,
|
|
687
|
-
success=False,
|
|
688
|
-
error=str(e)
|
|
689
|
-
))
|
|
690
|
-
|
|
691
|
-
state.browser_results.extend(error_results)
|
|
692
|
-
state.pending_tasks = []
|
|
693
|
-
state.pending_todo_indices = []
|
|
694
|
-
state.current_step = "supervisor_agent"
|
|
695
|
-
|
|
696
|
-
log_agent_activity(state, "browser_task_executor", "error", f"Browser execution failed: {str(e)}")
|
|
697
|
-
return state
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
async def report_task_execution_node(state: VibeSurfState) -> VibeSurfState:
|
|
701
|
-
"""
|
|
702
|
-
Execute HTML report generation task assigned by supervisor agent
|
|
703
|
-
"""
|
|
704
|
-
return await control_aware_node(_report_task_execution_node_impl, state, "report_task_execution")
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
async def _report_task_execution_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
708
|
-
"""Implementation of report task execution node"""
|
|
709
|
-
logger.info("📄 Executing HTML report generation task...")
|
|
710
|
-
|
|
711
|
-
# Log agent activity
|
|
712
|
-
log_agent_activity(state, "report_task_executor", "working", "Generating HTML report")
|
|
713
|
-
|
|
714
|
-
try:
|
|
715
|
-
# Use ReportWriterAgent to generate HTML report
|
|
716
|
-
report_writer = ReportWriterAgent(
|
|
717
|
-
llm=state.llm,
|
|
718
|
-
workspace_dir=state.task_dir
|
|
542
|
+
import traceback
|
|
543
|
+
traceback.print_exc()
|
|
544
|
+
state.browser_results.append(BrowserTaskResult(
|
|
545
|
+
agent_id="unknown",
|
|
546
|
+
agent_workdir="unknown",
|
|
547
|
+
task='unknown',
|
|
548
|
+
success=False,
|
|
549
|
+
error=str(e)
|
|
719
550
|
)
|
|
551
|
+
)
|
|
552
|
+
state.current_step = "vibesurf_agent"
|
|
720
553
|
|
|
721
|
-
|
|
722
|
-
"original_task": state.original_task,
|
|
723
|
-
"execution_results": state.browser_results,
|
|
724
|
-
"report_type": "detailed", # Default to detailed report
|
|
725
|
-
"upload_files": state.upload_files
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
report_path = await report_writer.generate_report(report_data)
|
|
729
|
-
|
|
730
|
-
state.generated_report_path = report_path
|
|
731
|
-
|
|
732
|
-
# Return to supervisor for next decision
|
|
733
|
-
state.current_step = "supervisor_agent"
|
|
734
|
-
|
|
735
|
-
log_agent_activity(state, "report_task_executor", "result",
|
|
736
|
-
f"HTML report generated successfully at: `{report_path}`")
|
|
737
|
-
|
|
738
|
-
logger.info(f"✅ Report generated: {report_path}")
|
|
739
|
-
return state
|
|
740
|
-
|
|
741
|
-
except Exception as e:
|
|
742
|
-
logger.error(f"❌ Report generation failed: {e}")
|
|
743
|
-
state.current_step = "supervisor_agent"
|
|
744
|
-
log_agent_activity(state, "report_task_executor", "error", f"Report generation failed: {str(e)}")
|
|
554
|
+
await log_agent_activity(state, "browser_task_executor", "error", f"Browser execution failed: {str(e)}")
|
|
745
555
|
return state
|
|
746
556
|
|
|
747
557
|
|
|
748
|
-
async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTaskResult]:
|
|
558
|
+
async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTaskResult] | None:
|
|
749
559
|
"""Execute pending tasks in parallel using multiple browser agents"""
|
|
750
560
|
logger.info("🔄 Executing pending tasks in parallel...")
|
|
751
561
|
|
|
752
562
|
# Register agents with browser manager
|
|
753
563
|
agents = []
|
|
754
|
-
pending_tasks = state.
|
|
564
|
+
pending_tasks = state.browser_tasks
|
|
755
565
|
bu_agent_ids = []
|
|
756
566
|
register_sessions = []
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
567
|
+
task_id = nanoid.generate(size=5)
|
|
568
|
+
bu_agents_workdir = state.vibesurf_agent.file_system.get_dir() / "bu_agents"
|
|
569
|
+
bu_agents_workdir.mkdir(parents=True, exist_ok=True)
|
|
570
|
+
|
|
571
|
+
for i, task_info in enumerate(pending_tasks):
|
|
572
|
+
agent_id = f"bu_agent-{task_id}-{i + 1:03d}"
|
|
573
|
+
task_description = task_info.get('task', '')
|
|
574
|
+
if not task_description:
|
|
575
|
+
continue
|
|
576
|
+
target_id = task_info.get('target_id', None)
|
|
764
577
|
register_sessions.append(
|
|
765
|
-
state.browser_manager.register_agent(agent_id, target_id=target_id)
|
|
578
|
+
state.vibesurf_agent.browser_manager.register_agent(agent_id, target_id=target_id)
|
|
766
579
|
)
|
|
767
580
|
bu_agent_ids.append(agent_id)
|
|
768
581
|
agent_browser_sessions = await asyncio.gather(*register_sessions)
|
|
769
582
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
583
|
+
vibesurf_tools = state.vibesurf_agent.tools
|
|
584
|
+
bu_tools = BrowserUseTools()
|
|
585
|
+
for mcp_server_name, mcp_client in vibesurf_tools.mcp_clients.items():
|
|
586
|
+
await mcp_client.register_to_tools(
|
|
587
|
+
tools=bu_tools,
|
|
588
|
+
prefix=f"mcp.{mcp_server_name}."
|
|
589
|
+
)
|
|
590
|
+
bu_tasks = [None] * len(pending_tasks)
|
|
591
|
+
for i, task_info in enumerate(pending_tasks):
|
|
592
|
+
agent_id = f"bu_agent-{task_id}-{i + 1:03d}"
|
|
593
|
+
task_description = task_info.get('task', '')
|
|
594
|
+
if not task_description:
|
|
595
|
+
continue
|
|
596
|
+
task_files = task_info.get('task_files', [])
|
|
597
|
+
bu_agent_workdir = bu_agents_workdir / f"{task_id}-{i + 1:03d}"
|
|
598
|
+
bu_agent_workdir.mkdir(parents=True, exist_ok=True)
|
|
599
|
+
agent_name = f"browser_use_agent-{task_id}-{i + 1:03d}"
|
|
600
|
+
# Log agent creation
|
|
601
|
+
await log_agent_activity(state, agent_name, "working", f"{task_description}")
|
|
602
|
+
|
|
776
603
|
try:
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
604
|
+
available_file_paths = []
|
|
605
|
+
if task_files:
|
|
606
|
+
for task_file in task_files:
|
|
607
|
+
upload_workdir = bu_agent_workdir / "upload_files"
|
|
608
|
+
upload_workdir.mkdir(parents=True, exist_ok=True)
|
|
609
|
+
task_file_path = state.vibesurf_agent.file_system.get_absolute_path(task_file)
|
|
610
|
+
if os.path.exists(task_file_path):
|
|
611
|
+
logger.info(f"Copy {task_file_path} to {upload_workdir}")
|
|
612
|
+
shutil.copy(task_file_path, str(upload_workdir))
|
|
613
|
+
available_file_paths.append(os.path.join("upload_files", os.path.basename(task_file_path)))
|
|
780
614
|
|
|
781
615
|
# Create BrowserUseAgent for each task
|
|
782
|
-
if
|
|
783
|
-
upload_files_md =
|
|
784
|
-
bu_task = task_description + f"\
|
|
616
|
+
if available_file_paths:
|
|
617
|
+
upload_files_md = '\n'.join(available_file_paths)
|
|
618
|
+
bu_task = task_description + f"\nNecessary files for this task:\n{upload_files_md}\n"
|
|
785
619
|
else:
|
|
786
620
|
bu_task = task_description
|
|
787
|
-
|
|
621
|
+
bu_tasks[i] = bu_task
|
|
788
622
|
# Create step callback for this agent
|
|
789
|
-
agent_name = f"browser_use_agent-{i + 1}-{state.task_id[-4:]}"
|
|
790
623
|
step_callback = create_browser_agent_step_callback(state, agent_name)
|
|
791
|
-
|
|
792
624
|
agent = BrowserUseAgent(
|
|
793
625
|
task=bu_task,
|
|
794
|
-
llm=state.llm,
|
|
626
|
+
llm=state.vibesurf_agent.llm,
|
|
795
627
|
browser_session=agent_browser_sessions[i],
|
|
796
|
-
|
|
797
|
-
task_id=f"{
|
|
798
|
-
file_system_path=
|
|
628
|
+
tools=bu_tools,
|
|
629
|
+
task_id=f"{task_id}-{i + 1:03d}",
|
|
630
|
+
file_system_path=str(bu_agent_workdir),
|
|
799
631
|
register_new_step_callback=step_callback,
|
|
800
|
-
extend_system_message=
|
|
632
|
+
extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
|
|
633
|
+
token_cost_service=state.vibesurf_agent.token_cost_service
|
|
801
634
|
)
|
|
802
635
|
agents.append(agent)
|
|
803
636
|
|
|
@@ -807,9 +640,11 @@ async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTa
|
|
|
807
640
|
logger.debug(f"🔗 Registered parallel agent {agent_id} for control coordination")
|
|
808
641
|
|
|
809
642
|
except Exception as e:
|
|
643
|
+
import traceback
|
|
644
|
+
traceback.print_exc()
|
|
810
645
|
logger.error(f"❌ Failed to create agent {agent_id}: {e}")
|
|
811
|
-
log_agent_activity(state,
|
|
812
|
-
|
|
646
|
+
await log_agent_activity(state, agent_name, "error",
|
|
647
|
+
f"Failed to create agent: {str(e)}")
|
|
813
648
|
|
|
814
649
|
# Execute all agents in parallel
|
|
815
650
|
try:
|
|
@@ -818,155 +653,284 @@ async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTa
|
|
|
818
653
|
# Process results
|
|
819
654
|
results = []
|
|
820
655
|
for i, (agent, history) in enumerate(zip(agents, histories)):
|
|
821
|
-
|
|
656
|
+
task = bu_tasks[i]
|
|
657
|
+
bu_agent_workdir = f"bu_agents/{task_id}-{i + 1:03d}"
|
|
658
|
+
agent_name = f"browser_use_agent-{task_id}-{i + 1:03d}"
|
|
659
|
+
|
|
660
|
+
important_files = []
|
|
661
|
+
if history and history.history and len(history.history[-1].result) > 0:
|
|
662
|
+
last_result = history.history[-1].result[-1]
|
|
663
|
+
important_files = last_result.attachments
|
|
664
|
+
if important_files:
|
|
665
|
+
important_files = [os.path.join(bu_agent_workdir, file_name) for file_name in important_files]
|
|
666
|
+
|
|
822
667
|
if isinstance(history, Exception):
|
|
823
668
|
results.append(BrowserTaskResult(
|
|
824
|
-
agent_id=f"
|
|
825
|
-
task=
|
|
669
|
+
agent_id=f"{task_id}-{i + 1:03d}",
|
|
670
|
+
task=task,
|
|
826
671
|
success=False,
|
|
827
|
-
error=str(history)
|
|
672
|
+
error=str(history),
|
|
673
|
+
agent_workdir=bu_agent_workdir,
|
|
674
|
+
important_files=important_files,
|
|
828
675
|
))
|
|
829
676
|
# Log error
|
|
830
|
-
log_agent_activity(state, f"
|
|
831
|
-
f"Task failed: {str(history)}")
|
|
677
|
+
await log_agent_activity(state, agent_name, "error", f"Task failed: {str(history)}")
|
|
832
678
|
else:
|
|
833
679
|
results.append(BrowserTaskResult(
|
|
834
|
-
agent_id=f"
|
|
835
|
-
task=
|
|
680
|
+
agent_id=f"{task_id}-{i + 1:03d}",
|
|
681
|
+
task=task,
|
|
682
|
+
agent_workdir=bu_agent_workdir,
|
|
836
683
|
success=history.is_successful(),
|
|
684
|
+
important_files=important_files,
|
|
837
685
|
result=history.final_result() if hasattr(history, 'final_result') else "Task completed",
|
|
838
686
|
error=str(history.errors()) if history.has_errors() and not history.is_successful() else ""
|
|
839
687
|
))
|
|
840
688
|
# Log result
|
|
841
689
|
if history.is_successful():
|
|
842
|
-
result_text = history.final_result()
|
|
843
|
-
log_agent_activity(state,
|
|
844
|
-
|
|
690
|
+
result_text = history.final_result()
|
|
691
|
+
await log_agent_activity(state, agent_name, "result",
|
|
692
|
+
f"Task completed successfully: \n{result_text}")
|
|
845
693
|
else:
|
|
846
|
-
error_text = str(history.errors())
|
|
847
|
-
log_agent_activity(state, f"
|
|
848
|
-
f"Task failed: {error_text}")
|
|
694
|
+
error_text = str(history.errors())
|
|
695
|
+
await log_agent_activity(state, agent_name, "error", f"Task failed: {error_text}")
|
|
849
696
|
|
|
850
697
|
return results
|
|
851
698
|
|
|
699
|
+
except Exception as e:
|
|
700
|
+
import traceback
|
|
701
|
+
traceback.print_exc()
|
|
702
|
+
|
|
852
703
|
finally:
|
|
853
704
|
# Remove agents from control tracking and cleanup browser sessions
|
|
854
705
|
for i, agent_id in enumerate(bu_agent_ids):
|
|
855
706
|
if not isinstance(pending_tasks[i], list):
|
|
856
|
-
await state.browser_manager.unregister_agent(agent_id, close_tabs=True)
|
|
707
|
+
await state.vibesurf_agent.browser_manager.unregister_agent(agent_id, close_tabs=True)
|
|
857
708
|
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
858
709
|
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
859
710
|
logger.debug(f"🔗 Unregistered parallel agent {agent_id} from control coordination")
|
|
860
711
|
|
|
861
712
|
|
|
862
|
-
async def execute_single_browser_tasks(state: VibeSurfState) ->
|
|
713
|
+
async def execute_single_browser_tasks(state: VibeSurfState) -> BrowserTaskResult | None:
|
|
863
714
|
"""Execute pending tasks in single mode one by one"""
|
|
864
715
|
logger.info("🔄 Executing pending tasks in single mode...")
|
|
716
|
+
task_info = state.browser_tasks[0]
|
|
717
|
+
task_id = nanoid.generate(size=5)
|
|
718
|
+
bu_agents_workdir = state.vibesurf_agent.file_system.get_dir() / "bu_agents"
|
|
719
|
+
bu_agents_workdir.mkdir(parents=True, exist_ok=True)
|
|
720
|
+
task_description = task_info.get('task', '')
|
|
721
|
+
if not task_description:
|
|
722
|
+
return BrowserTaskResult(
|
|
723
|
+
agent_id=f"{task_id}-{1:03d}",
|
|
724
|
+
task='',
|
|
725
|
+
agent_workdir=f"bu_agents/{task_id}-{1:03d}",
|
|
726
|
+
success=False,
|
|
727
|
+
error="Task description is empty. Please provide a valid task description for browser use agent.",
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
task_files = task_info.get('task_files', [])
|
|
731
|
+
bu_agent_workdir = bu_agents_workdir / f"{task_id}-{1:03d}"
|
|
732
|
+
bu_agent_workdir.mkdir(parents=True, exist_ok=True)
|
|
733
|
+
agent_name = f"browser_use_agent-{task_id}-{1:03d}"
|
|
734
|
+
agent_id = f"bu_agent-{task_id}-{1:03d}"
|
|
735
|
+
# Log agent creation
|
|
736
|
+
await log_agent_activity(state, agent_name, "working", f"{task_description}")
|
|
865
737
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
738
|
+
try:
|
|
739
|
+
vibesurf_tools = state.vibesurf_agent.tools
|
|
740
|
+
bu_tools = BrowserUseTools()
|
|
741
|
+
for mcp_server_name, mcp_client in vibesurf_tools.mcp_clients.items():
|
|
742
|
+
await mcp_client.register_to_tools(
|
|
743
|
+
tools=bu_tools,
|
|
744
|
+
prefix=f"mcp.{mcp_server_name}."
|
|
745
|
+
)
|
|
746
|
+
available_file_paths = []
|
|
747
|
+
if task_files:
|
|
748
|
+
for task_file in task_files:
|
|
749
|
+
upload_workdir = bu_agent_workdir / "upload_files"
|
|
750
|
+
upload_workdir.mkdir(parents=True, exist_ok=True)
|
|
751
|
+
task_file_path = state.vibesurf_agent.file_system.get_absolute_path(task_file)
|
|
752
|
+
if os.path.exists(task_file_path):
|
|
753
|
+
logger.info(f"Copy {task_file_path} to {upload_workdir}")
|
|
754
|
+
shutil.copy(task_file_path, str(upload_workdir))
|
|
755
|
+
available_file_paths.append(os.path.join("upload_files", os.path.basename(task_file_path)))
|
|
756
|
+
|
|
757
|
+
# Create BrowserUseAgent for each task
|
|
758
|
+
if available_file_paths:
|
|
759
|
+
upload_files_md = '\n'.join(available_file_paths)
|
|
760
|
+
bu_task = task_description + f"\nNecessary files for this task:\n{upload_files_md}\n"
|
|
761
|
+
else:
|
|
762
|
+
bu_task = task_description
|
|
763
|
+
|
|
764
|
+
step_callback = create_browser_agent_step_callback(state, agent_name)
|
|
765
|
+
main_browser_session = state.vibesurf_agent.browser_manager.main_browser_session
|
|
766
|
+
if task_info.get("tab_id", None):
|
|
767
|
+
tab_id = task_info.get("tab_id")
|
|
768
|
+
target_id = await main_browser_session.get_target_id_from_tab_id(tab_id)
|
|
769
|
+
await main_browser_session.get_or_create_cdp_session(target_id=target_id)
|
|
870
770
|
else:
|
|
871
|
-
|
|
872
|
-
|
|
771
|
+
new_target = await main_browser_session.cdp_client.send.Target.createTarget(
|
|
772
|
+
params={'url': 'about:blank'})
|
|
773
|
+
target_id = new_target["targetId"]
|
|
774
|
+
await main_browser_session.get_or_create_cdp_session(target_id=target_id)
|
|
775
|
+
agent = BrowserUseAgent(
|
|
776
|
+
task=bu_task,
|
|
777
|
+
llm=state.vibesurf_agent.llm,
|
|
778
|
+
browser_session=main_browser_session,
|
|
779
|
+
tools=bu_tools,
|
|
780
|
+
task_id=f"{task_id}-{1:03d}",
|
|
781
|
+
file_system_path=str(bu_agent_workdir),
|
|
782
|
+
register_new_step_callback=step_callback,
|
|
783
|
+
extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
|
|
784
|
+
token_cost_service=state.vibesurf_agent.token_cost_service
|
|
785
|
+
)
|
|
786
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
787
|
+
state.vibesurf_agent._running_agents[agent_id] = agent
|
|
788
|
+
logger.debug(f"🔗 Registered single agent {agent_id} for control coordination")
|
|
789
|
+
|
|
790
|
+
history = await agent.run()
|
|
791
|
+
bu_agent_workdir = f"bu_agents/{task_id}-{1:03d}"
|
|
792
|
+
important_files = []
|
|
793
|
+
if history and history.history and len(history.history[-1].result) > 0:
|
|
794
|
+
last_result = history.history[-1].result[-1]
|
|
795
|
+
important_files = last_result.attachments
|
|
796
|
+
if important_files:
|
|
797
|
+
important_files = [os.path.join(bu_agent_workdir, file_name) for file_name in important_files]
|
|
798
|
+
|
|
799
|
+
result = BrowserTaskResult(
|
|
800
|
+
agent_id=agent_id,
|
|
801
|
+
agent_workdir=bu_agent_workdir,
|
|
802
|
+
task=bu_task,
|
|
803
|
+
important_files=important_files,
|
|
804
|
+
success=history.is_successful(),
|
|
805
|
+
result=history.final_result() if hasattr(history, 'final_result') else "Task completed",
|
|
806
|
+
error=str(history.errors()) if history.has_errors() and not history.is_successful() else ""
|
|
807
|
+
)
|
|
873
808
|
|
|
874
|
-
|
|
809
|
+
# Log result
|
|
810
|
+
if result.success:
|
|
811
|
+
await log_agent_activity(state, agent_name, "result",
|
|
812
|
+
f"Task completed successfully: \n{result.result}")
|
|
813
|
+
else:
|
|
814
|
+
await log_agent_activity(state, agent_name, "error",
|
|
815
|
+
f"Task failed: {result.error}")
|
|
816
|
+
return result
|
|
875
817
|
|
|
876
|
-
|
|
877
|
-
|
|
818
|
+
except Exception as e:
|
|
819
|
+
import traceback
|
|
820
|
+
traceback.print_exc()
|
|
821
|
+
|
|
822
|
+
bu_agent_workdir = f"bu_agents/{task_id}-{1:03d}"
|
|
823
|
+
return BrowserTaskResult(
|
|
824
|
+
agent_id=agent_id,
|
|
825
|
+
agent_workdir=bu_agent_workdir,
|
|
826
|
+
task=task_description,
|
|
827
|
+
success=False,
|
|
828
|
+
error=str(e)
|
|
829
|
+
)
|
|
830
|
+
finally:
|
|
831
|
+
# Remove agent from control tracking
|
|
832
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
833
|
+
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
834
|
+
logger.debug(f"🔗 Unregistered single agent {agent_id} from control coordination")
|
|
878
835
|
|
|
879
|
-
try:
|
|
880
|
-
await state.browser_manager._get_active_target()
|
|
881
|
-
if state.upload_files:
|
|
882
|
-
upload_files_md = format_upload_files_list(state.upload_files)
|
|
883
|
-
bu_task = task_description + f"\nAvailable user uploaded files:\n{upload_files_md}\n"
|
|
884
|
-
else:
|
|
885
|
-
bu_task = task_description
|
|
886
|
-
# Create step callback for this agent
|
|
887
|
-
agent_name = f"browser_use_agent-{state.task_id[-4:]}"
|
|
888
|
-
step_callback = create_browser_agent_step_callback(state, agent_name)
|
|
889
|
-
|
|
890
|
-
agent = BrowserUseAgent(
|
|
891
|
-
task=bu_task,
|
|
892
|
-
llm=state.llm,
|
|
893
|
-
browser_session=state.browser_manager.main_browser_session,
|
|
894
|
-
controller=state.vibesurf_controller,
|
|
895
|
-
task_id=f"{state.task_id}-{i}",
|
|
896
|
-
file_system_path=state.task_dir,
|
|
897
|
-
register_new_step_callback=step_callback,
|
|
898
|
-
extend_system_message="Please make sure the language of your output in JSON values should remain the same as the user's request or task."
|
|
899
|
-
)
|
|
900
836
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
837
|
+
async def report_task_execution_node(state: VibeSurfState) -> VibeSurfState:
|
|
838
|
+
"""
|
|
839
|
+
Execute HTML report generation task assigned by supervisor agent
|
|
840
|
+
"""
|
|
841
|
+
return await control_aware_node(_report_task_execution_node_impl, state, "report_task_execution")
|
|
905
842
|
|
|
906
|
-
try:
|
|
907
|
-
history = await agent.run()
|
|
908
843
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
success=history.is_successful(),
|
|
913
|
-
result=history.final_result() if hasattr(history, 'final_result') else "Task completed",
|
|
914
|
-
error=str(history.errors()) if history.has_errors() and not history.is_successful() else ""
|
|
915
|
-
)
|
|
844
|
+
async def _report_task_execution_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
845
|
+
"""Implementation of report task execution node"""
|
|
846
|
+
logger.info("📄 Executing HTML report generation task...")
|
|
916
847
|
|
|
917
|
-
|
|
918
|
-
if result.success:
|
|
919
|
-
log_agent_activity(state, f"browser_use_agent-{state.task_id[-4:]}", "result",
|
|
920
|
-
f"Task completed successfully: \n{result.result}")
|
|
921
|
-
else:
|
|
922
|
-
log_agent_activity(state, f"browser_use_agent-{state.task_id[-4:]}", "error",
|
|
923
|
-
f"Task failed: {result.error}")
|
|
848
|
+
agent_name = "report_writer_agent"
|
|
924
849
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
# Remove agent from control tracking
|
|
928
|
-
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
929
|
-
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
930
|
-
logger.debug(f"🔗 Unregistered single agent {agent_id} from control coordination")
|
|
850
|
+
# Log agent activity
|
|
851
|
+
await log_agent_activity(state, agent_name, "working", "Generating HTML report")
|
|
931
852
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
853
|
+
try:
|
|
854
|
+
# Create step callback for report writer agent
|
|
855
|
+
step_callback = create_report_writer_step_callback(state, agent_name)
|
|
856
|
+
|
|
857
|
+
# Use ReportWriterAgent to generate HTML report
|
|
858
|
+
report_writer = ReportWriterAgent(
|
|
859
|
+
llm=state.vibesurf_agent.llm,
|
|
860
|
+
workspace_dir=str(state.vibesurf_agent.file_system.get_dir()),
|
|
861
|
+
step_callback=step_callback,
|
|
862
|
+
thinking_mode=state.vibesurf_agent.thinking_mode,
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
# Register report writer agent for control coordination
|
|
866
|
+
agent_id = "report_writer_agent"
|
|
867
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
868
|
+
state.vibesurf_agent._running_agents[agent_id] = report_writer
|
|
869
|
+
logger.debug(f"🔗 Registered report writer agent for control coordination")
|
|
870
|
+
|
|
871
|
+
try:
|
|
872
|
+
action_params = state.action_params
|
|
873
|
+
report_task = action_params.get('task', [])
|
|
874
|
+
report_information = {
|
|
875
|
+
"browser_results": [bu_result.model_dump() for bu_result in state.browser_results if bu_result]
|
|
876
|
+
}
|
|
877
|
+
report_data = {
|
|
878
|
+
"report_task": report_task,
|
|
879
|
+
"report_information": report_information
|
|
880
|
+
}
|
|
942
881
|
|
|
943
|
-
|
|
882
|
+
report_result = await report_writer.generate_report(report_data)
|
|
883
|
+
state.generated_report_result = report_result
|
|
944
884
|
|
|
885
|
+
# Return to vibesurf agent for next decision
|
|
886
|
+
state.current_step = "vibesurf_agent"
|
|
945
887
|
|
|
946
|
-
|
|
947
|
-
|
|
888
|
+
if report_result.success:
|
|
889
|
+
await log_agent_activity(state, agent_name, "result",
|
|
890
|
+
f"✅ HTML report generated successfully: {report_result.msg}\nPath: `{report_result.report_path}`")
|
|
891
|
+
logger.info(f"✅ Report generated successfully: {report_result.report_path}")
|
|
892
|
+
else:
|
|
893
|
+
await log_agent_activity(state, agent_name, "error",
|
|
894
|
+
f"❌ Report generation failed: {report_result.msg}\nPath: `{report_result.report_path}`")
|
|
895
|
+
logger.warning(f"⚠️ Report generation failed: {report_result.msg}")
|
|
896
|
+
|
|
897
|
+
finally:
|
|
898
|
+
# Remove report writer agent from control tracking
|
|
899
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
900
|
+
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
901
|
+
logger.debug(f"🔗 Unregistered report writer agent from control coordination")
|
|
902
|
+
|
|
903
|
+
return state
|
|
904
|
+
|
|
905
|
+
except Exception as e:
|
|
906
|
+
logger.error(f"❌ Report generation failed: {e}")
|
|
907
|
+
state.current_step = "vibesurf_agent"
|
|
908
|
+
await log_agent_activity(state, agent_name, "error", f"Report generation failed: {str(e)}")
|
|
909
|
+
return state
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def route_after_vibesurf_agent(state: VibeSurfState) -> str:
|
|
913
|
+
"""Route based on vibesurf agent decisions"""
|
|
948
914
|
if state.current_step == "browser_task_execution":
|
|
949
915
|
return "browser_task_execution"
|
|
950
916
|
elif state.current_step == "report_task_execution":
|
|
951
917
|
return "report_task_execution"
|
|
952
|
-
elif state.current_step == "
|
|
953
|
-
return "
|
|
954
|
-
elif state.
|
|
955
|
-
return "
|
|
956
|
-
elif state.current_step == "supervisor_agent":
|
|
957
|
-
return "supervisor_agent" # Continue in supervisor loop
|
|
918
|
+
elif state.current_step == "vibesurf_agent":
|
|
919
|
+
return "vibesurf_agent" # Continue in vibesurf agent loop
|
|
920
|
+
elif state.is_complete:
|
|
921
|
+
return "END" # task_done sets is_complete=True, go directly to END
|
|
958
922
|
else:
|
|
959
923
|
return "END" # Default fallback - complete workflow
|
|
960
924
|
|
|
961
925
|
|
|
962
926
|
def route_after_browser_task_execution(state: VibeSurfState) -> str:
|
|
963
|
-
"""Route back to
|
|
964
|
-
return "
|
|
927
|
+
"""Route back to vibesurf agent after browser task completion"""
|
|
928
|
+
return "vibesurf_agent"
|
|
965
929
|
|
|
966
930
|
|
|
967
931
|
def route_after_report_task_execution(state: VibeSurfState) -> str:
|
|
968
|
-
"""Route back to
|
|
969
|
-
return "
|
|
932
|
+
"""Route back to vibesurf agent after report task completion"""
|
|
933
|
+
return "vibesurf_agent"
|
|
970
934
|
|
|
971
935
|
|
|
972
936
|
def should_continue(state: VibeSurfState) -> str:
|
|
@@ -978,38 +942,36 @@ def should_continue(state: VibeSurfState) -> str:
|
|
|
978
942
|
|
|
979
943
|
|
|
980
944
|
def create_vibe_surf_workflow() -> StateGraph:
|
|
981
|
-
"""Create the simplified LangGraph workflow with supervisor agent as core
|
|
945
|
+
"""Create the simplified LangGraph workflow with supervisor agent as core tools"""
|
|
982
946
|
|
|
983
947
|
workflow = StateGraph(VibeSurfState)
|
|
984
948
|
|
|
985
949
|
# Add nodes for simplified architecture
|
|
986
|
-
workflow.add_node("
|
|
950
|
+
workflow.add_node("vibesurf_agent", vibesurf_agent_node)
|
|
987
951
|
workflow.add_node("browser_task_execution", browser_task_execution_node)
|
|
988
952
|
workflow.add_node("report_task_execution", report_task_execution_node)
|
|
989
953
|
|
|
990
954
|
# Set entry point
|
|
991
|
-
workflow.set_entry_point("
|
|
955
|
+
workflow.set_entry_point("vibesurf_agent")
|
|
992
956
|
|
|
993
|
-
#
|
|
957
|
+
# VibeSurf agent routes to different execution nodes or END
|
|
994
958
|
workflow.add_conditional_edges(
|
|
995
|
-
"
|
|
996
|
-
|
|
959
|
+
"vibesurf_agent",
|
|
960
|
+
route_after_vibesurf_agent,
|
|
997
961
|
{
|
|
998
962
|
"browser_task_execution": "browser_task_execution",
|
|
999
963
|
"report_task_execution": "report_task_execution",
|
|
1000
|
-
"
|
|
1001
|
-
"supervisor_agent": "supervisor_agent",
|
|
1002
|
-
"simple_response": END,
|
|
964
|
+
"vibesurf_agent": "vibesurf_agent",
|
|
1003
965
|
"END": END
|
|
1004
966
|
}
|
|
1005
967
|
)
|
|
1006
968
|
|
|
1007
|
-
# Execution nodes return to
|
|
969
|
+
# Execution nodes return to vibesurf agent
|
|
1008
970
|
workflow.add_conditional_edges(
|
|
1009
971
|
"browser_task_execution",
|
|
1010
972
|
route_after_browser_task_execution,
|
|
1011
973
|
{
|
|
1012
|
-
"
|
|
974
|
+
"vibesurf_agent": "vibesurf_agent"
|
|
1013
975
|
}
|
|
1014
976
|
)
|
|
1015
977
|
|
|
@@ -1017,7 +979,7 @@ def create_vibe_surf_workflow() -> StateGraph:
|
|
|
1017
979
|
"report_task_execution",
|
|
1018
980
|
route_after_report_task_execution,
|
|
1019
981
|
{
|
|
1020
|
-
"
|
|
982
|
+
"vibesurf_agent": "vibesurf_agent"
|
|
1021
983
|
}
|
|
1022
984
|
)
|
|
1023
985
|
|
|
@@ -1025,24 +987,32 @@ def create_vibe_surf_workflow() -> StateGraph:
|
|
|
1025
987
|
|
|
1026
988
|
|
|
1027
989
|
class VibeSurfAgent:
|
|
1028
|
-
"""Main LangGraph-based
|
|
990
|
+
"""Main LangGraph-based VibeSurf Agent"""
|
|
1029
991
|
|
|
1030
992
|
def __init__(
|
|
1031
993
|
self,
|
|
1032
994
|
llm: BaseChatModel,
|
|
1033
995
|
browser_manager: BrowserManager,
|
|
1034
|
-
|
|
996
|
+
tools: VibeSurfTools,
|
|
1035
997
|
workspace_dir: str = "./workspace",
|
|
998
|
+
thinking_mode: bool = True,
|
|
999
|
+
calculate_token_cost: bool = True,
|
|
1036
1000
|
):
|
|
1037
1001
|
"""Initialize VibeSurfAgent with required components"""
|
|
1038
|
-
self.llm = llm
|
|
1039
|
-
self.
|
|
1040
|
-
self.
|
|
1002
|
+
self.llm: BaseChatModel = llm
|
|
1003
|
+
self.calculate_token_cost = calculate_token_cost
|
|
1004
|
+
self.token_cost_service = TokenCost(include_cost=calculate_token_cost)
|
|
1005
|
+
self.token_cost_service.register_llm(llm)
|
|
1006
|
+
self.browser_manager: BrowserManager = browser_manager
|
|
1007
|
+
self.tools: VibeSurfTools = tools
|
|
1041
1008
|
self.workspace_dir = workspace_dir
|
|
1042
1009
|
os.makedirs(self.workspace_dir, exist_ok=True)
|
|
1010
|
+
self.thinking_mode = thinking_mode
|
|
1011
|
+
|
|
1043
1012
|
self.cur_session_id = None
|
|
1044
|
-
self.
|
|
1045
|
-
self.
|
|
1013
|
+
self.file_system: Optional[CustomFileSystem] = None
|
|
1014
|
+
self.message_history = []
|
|
1015
|
+
self.activity_logs = []
|
|
1046
1016
|
|
|
1047
1017
|
# Create LangGraph workflow
|
|
1048
1018
|
self.workflow = create_vibe_surf_workflow()
|
|
@@ -1054,47 +1024,96 @@ class VibeSurfAgent:
|
|
|
1054
1024
|
self._running_agents: Dict[str, Any] = {} # Track running BrowserUseAgent instances
|
|
1055
1025
|
self._execution_task: Optional[asyncio.Task] = None
|
|
1056
1026
|
|
|
1057
|
-
logger.info("🌊
|
|
1058
|
-
|
|
1059
|
-
def load_message_history(self,
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
return
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1027
|
+
logger.info("🌊 VibeSurf Agent initialized with LangGraph workflow")
|
|
1028
|
+
|
|
1029
|
+
def load_message_history(self, session_id: Optional[str] = None) -> list:
|
|
1030
|
+
"""Load message history for a specific session, or return [] for new sessions"""
|
|
1031
|
+
if session_id is None:
|
|
1032
|
+
return []
|
|
1033
|
+
|
|
1034
|
+
session_message_history_path = os.path.join(self.workspace_dir, "sessions", session_id, "message_history.pkl")
|
|
1035
|
+
|
|
1036
|
+
if not os.path.exists(session_message_history_path):
|
|
1037
|
+
all_message_history_path = os.path.join(self.workspace_dir, "message_history.pkl")
|
|
1038
|
+
if os.path.exists(all_message_history_path):
|
|
1039
|
+
with open(all_message_history_path, "rb") as f:
|
|
1040
|
+
message_history_dict = pickle.load(f)
|
|
1041
|
+
if session_id in message_history_dict:
|
|
1042
|
+
return message_history_dict[session_id]
|
|
1043
|
+
logger.info(f"No message history found for session {session_id}, creating new")
|
|
1044
|
+
return []
|
|
1045
|
+
|
|
1046
|
+
try:
|
|
1047
|
+
with open(session_message_history_path, "rb") as f:
|
|
1048
|
+
message_history = pickle.load(f)
|
|
1049
|
+
logger.info(f"Loading message history for session {session_id} from {session_message_history_path}")
|
|
1050
|
+
return message_history
|
|
1051
|
+
except Exception as e:
|
|
1052
|
+
logger.error(f"Failed to load message history for session {session_id}: {e}")
|
|
1053
|
+
return []
|
|
1054
|
+
|
|
1055
|
+
def save_message_history(self, session_id: Optional[str] = None):
|
|
1056
|
+
"""Save message history for a specific session"""
|
|
1057
|
+
if session_id is None:
|
|
1058
|
+
return
|
|
1059
|
+
|
|
1060
|
+
# Create session directory if it doesn't exist
|
|
1061
|
+
session_dir = os.path.join(self.workspace_dir, "sessions", session_id)
|
|
1062
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
1063
|
+
|
|
1064
|
+
session_message_history_path = os.path.join(session_dir, "message_history.pkl")
|
|
1065
|
+
|
|
1066
|
+
try:
|
|
1067
|
+
with open(session_message_history_path, "wb") as f:
|
|
1068
|
+
logger.info(f"Saving message history for session {session_id} to {session_message_history_path}")
|
|
1069
|
+
pickle.dump(self.message_history, f)
|
|
1070
|
+
except Exception as e:
|
|
1071
|
+
logger.error(f"Failed to save message history for session {session_id}: {e}")
|
|
1072
|
+
|
|
1073
|
+
def load_activity_logs(self, session_id: Optional[str] = None) -> list:
|
|
1074
|
+
"""Load activity logs for a specific session, or return [] for new sessions"""
|
|
1075
|
+
if session_id is None:
|
|
1076
|
+
return []
|
|
1077
|
+
|
|
1078
|
+
session_activity_logs_path = os.path.join(self.workspace_dir, "sessions", session_id, "activity_logs.pkl")
|
|
1079
|
+
|
|
1080
|
+
if not os.path.exists(session_activity_logs_path):
|
|
1081
|
+
# Adaptive to the older version
|
|
1082
|
+
all_activity_logs_path = os.path.join(self.workspace_dir, "activity_logs.pkl")
|
|
1083
|
+
if os.path.exists(all_activity_logs_path):
|
|
1084
|
+
with open(all_activity_logs_path, "rb") as f:
|
|
1085
|
+
activity_logs_dict = pickle.load(f)
|
|
1086
|
+
if session_id in activity_logs_dict:
|
|
1087
|
+
return activity_logs_dict[session_id]
|
|
1088
|
+
logger.info(f"No activity logs found for session {session_id}, creating new")
|
|
1089
|
+
return []
|
|
1090
|
+
|
|
1091
|
+
try:
|
|
1092
|
+
with open(session_activity_logs_path, "rb") as f:
|
|
1093
|
+
activity_logs = pickle.load(f)
|
|
1094
|
+
logger.info(f"Loading activity logs for session {session_id} from {session_activity_logs_path}")
|
|
1095
|
+
return activity_logs
|
|
1096
|
+
except Exception as e:
|
|
1097
|
+
logger.error(f"Failed to load activity logs for session {session_id}: {e}")
|
|
1098
|
+
return []
|
|
1099
|
+
|
|
1100
|
+
def save_activity_logs(self, session_id: Optional[str] = None):
|
|
1101
|
+
"""Save activity logs for a specific session"""
|
|
1102
|
+
if session_id is None:
|
|
1103
|
+
return
|
|
1104
|
+
|
|
1105
|
+
# Create session directory if it doesn't exist
|
|
1106
|
+
session_dir = os.path.join(self.workspace_dir, "sessions", session_id)
|
|
1107
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
1108
|
+
|
|
1109
|
+
session_activity_logs_path = os.path.join(session_dir, "activity_logs.pkl")
|
|
1110
|
+
|
|
1111
|
+
try:
|
|
1112
|
+
with open(session_activity_logs_path, "wb") as f:
|
|
1113
|
+
logger.info(f"Saving activity logs for session {session_id} to {session_activity_logs_path}")
|
|
1114
|
+
pickle.dump(self.activity_logs, f)
|
|
1115
|
+
except Exception as e:
|
|
1116
|
+
logger.error(f"Failed to save activity logs for session {session_id}: {e}")
|
|
1098
1117
|
|
|
1099
1118
|
async def stop(self, reason: str = None) -> ControlResult:
|
|
1100
1119
|
"""
|
|
@@ -1111,21 +1130,16 @@ class VibeSurfAgent:
|
|
|
1111
1130
|
reason = reason or "Manual stop requested"
|
|
1112
1131
|
logger.info(f"🛑 Stopping agent execution: {reason}")
|
|
1113
1132
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
supervisor_message_history.append(UserMessage(
|
|
1117
|
-
content=f"🛑 Stopping agent execution: {reason}"))
|
|
1133
|
+
self.message_history.append(UserMessage(
|
|
1134
|
+
content=f"🛑 Stopping agent execution: {reason}"))
|
|
1118
1135
|
|
|
1119
1136
|
if self._current_state:
|
|
1120
1137
|
self._current_state.should_stop = True
|
|
1121
1138
|
self._current_state.stopped = True
|
|
1122
|
-
self._current_state.control_timestamps["stopped"] = datetime.now()
|
|
1123
|
-
self._current_state.control_reasons["stopped"] = reason
|
|
1124
|
-
self._current_state.last_control_action = "stop"
|
|
1125
1139
|
|
|
1126
1140
|
# Stop all running agents with timeout
|
|
1127
1141
|
try:
|
|
1128
|
-
await asyncio.wait_for(self._stop_all_agents(
|
|
1142
|
+
await asyncio.wait_for(self._stop_all_agents(), timeout=3.0)
|
|
1129
1143
|
except asyncio.TimeoutError:
|
|
1130
1144
|
logger.warning("⚠️ Agent stopping timed out, continuing with task cancellation")
|
|
1131
1145
|
|
|
@@ -1176,19 +1190,14 @@ class VibeSurfAgent:
|
|
|
1176
1190
|
reason = reason or "Manual pause requested"
|
|
1177
1191
|
logger.info(f"⏸️ Pausing agent execution: {reason}")
|
|
1178
1192
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
supervisor_message_history.append(UserMessage(
|
|
1182
|
-
content=f"⏸️ Pausing agent execution: {reason}"))
|
|
1193
|
+
self.message_history.append(UserMessage(
|
|
1194
|
+
content=f"⏸️ Pausing agent execution: {reason}"))
|
|
1183
1195
|
|
|
1184
1196
|
if self._current_state:
|
|
1185
1197
|
self._current_state.should_pause = True
|
|
1186
|
-
self._current_state.control_timestamps["pause_requested"] = datetime.now()
|
|
1187
|
-
self._current_state.control_reasons["paused"] = reason
|
|
1188
|
-
self._current_state.last_control_action = "pause"
|
|
1189
1198
|
|
|
1190
1199
|
# Pause all running agents
|
|
1191
|
-
await self._pause_all_agents(
|
|
1200
|
+
await self._pause_all_agents()
|
|
1192
1201
|
|
|
1193
1202
|
logger.info(f"✅ VibeSurf execution paused: {reason}")
|
|
1194
1203
|
return ControlResult(
|
|
@@ -1221,20 +1230,15 @@ class VibeSurfAgent:
|
|
|
1221
1230
|
reason = reason or "Manual resume requested"
|
|
1222
1231
|
logger.info(f"▶️ Resuming agent execution: {reason}")
|
|
1223
1232
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
supervisor_message_history.append(UserMessage(
|
|
1227
|
-
content=f"▶️ Resuming agent execution: {reason}"))
|
|
1233
|
+
self.message_history.append(UserMessage(
|
|
1234
|
+
content=f"▶️ Resuming agent execution: {reason}"))
|
|
1228
1235
|
|
|
1229
1236
|
if self._current_state:
|
|
1230
1237
|
self._current_state.paused = False
|
|
1231
1238
|
self._current_state.should_pause = False
|
|
1232
|
-
self._current_state.control_timestamps["resumed"] = datetime.now()
|
|
1233
|
-
self._current_state.control_reasons["resumed"] = reason
|
|
1234
|
-
self._current_state.last_control_action = "resume"
|
|
1235
1239
|
|
|
1236
1240
|
# Resume all paused agents
|
|
1237
|
-
await self._resume_all_agents(
|
|
1241
|
+
await self._resume_all_agents()
|
|
1238
1242
|
|
|
1239
1243
|
logger.info(f"✅ VibeSurf execution resumed: {reason}")
|
|
1240
1244
|
return ControlResult(
|
|
@@ -1268,15 +1272,6 @@ class VibeSurfAgent:
|
|
|
1268
1272
|
reason = reason or f"Manual pause requested for agent {agent_id}"
|
|
1269
1273
|
logger.info(f"⏸️ Pausing agent {agent_id}: {reason}")
|
|
1270
1274
|
|
|
1271
|
-
# Update state tracking
|
|
1272
|
-
if self._current_state:
|
|
1273
|
-
self._current_state.paused_agents.add(agent_id)
|
|
1274
|
-
if agent_id not in self._current_state.agent_control_states:
|
|
1275
|
-
self._current_state.agent_control_states[agent_id] = {}
|
|
1276
|
-
self._current_state.agent_control_states[agent_id]["paused"] = True
|
|
1277
|
-
self._current_state.agent_control_states[agent_id]["pause_reason"] = reason
|
|
1278
|
-
self._current_state.agent_control_states[agent_id]["pause_timestamp"] = datetime.now()
|
|
1279
|
-
|
|
1280
1275
|
# Pause the specific agent if it's running
|
|
1281
1276
|
agent = self._running_agents.get(agent_id)
|
|
1282
1277
|
if agent:
|
|
@@ -1317,15 +1312,6 @@ class VibeSurfAgent:
|
|
|
1317
1312
|
reason = reason or f"Manual resume requested for agent {agent_id}"
|
|
1318
1313
|
logger.info(f"▶️ Resuming agent {agent_id}: {reason}")
|
|
1319
1314
|
|
|
1320
|
-
# Update state tracking
|
|
1321
|
-
if self._current_state:
|
|
1322
|
-
self._current_state.paused_agents.discard(agent_id)
|
|
1323
|
-
if agent_id not in self._current_state.agent_control_states:
|
|
1324
|
-
self._current_state.agent_control_states[agent_id] = {}
|
|
1325
|
-
self._current_state.agent_control_states[agent_id]["paused"] = False
|
|
1326
|
-
self._current_state.agent_control_states[agent_id]["resume_reason"] = reason
|
|
1327
|
-
self._current_state.agent_control_states[agent_id]["resume_timestamp"] = datetime.now()
|
|
1328
|
-
|
|
1329
1315
|
# Resume the specific agent if it's running
|
|
1330
1316
|
agent = self._running_agents.get(agent_id)
|
|
1331
1317
|
if agent:
|
|
@@ -1366,7 +1352,7 @@ class VibeSurfAgent:
|
|
|
1366
1352
|
elif self._current_state.paused or self._current_state.should_pause:
|
|
1367
1353
|
overall_status = "paused"
|
|
1368
1354
|
elif self._current_state.is_complete:
|
|
1369
|
-
overall_status = "
|
|
1355
|
+
overall_status = "completed"
|
|
1370
1356
|
else:
|
|
1371
1357
|
overall_status = "running"
|
|
1372
1358
|
|
|
@@ -1380,13 +1366,11 @@ class VibeSurfAgent:
|
|
|
1380
1366
|
error_message = None
|
|
1381
1367
|
pause_reason = None
|
|
1382
1368
|
|
|
1383
|
-
#
|
|
1384
|
-
if self._current_state and
|
|
1385
|
-
status = "paused"
|
|
1386
|
-
agent_state = self._current_state.agent_control_states.get(agent_id, {})
|
|
1387
|
-
pause_reason = agent_state.get("pause_reason")
|
|
1388
|
-
elif self._current_state and self._current_state.stopped:
|
|
1369
|
+
# Simplified status checking since paused_agents removed
|
|
1370
|
+
if self._current_state and self._current_state.stopped:
|
|
1389
1371
|
status = "stopped"
|
|
1372
|
+
elif self._current_state and self._current_state.paused:
|
|
1373
|
+
status = "paused"
|
|
1390
1374
|
|
|
1391
1375
|
# Get current action if available
|
|
1392
1376
|
if agent and hasattr(agent, 'state'):
|
|
@@ -1411,11 +1395,9 @@ class VibeSurfAgent:
|
|
|
1411
1395
|
if self._current_state:
|
|
1412
1396
|
progress = {
|
|
1413
1397
|
"current_step": self._current_state.current_step,
|
|
1414
|
-
"completed_todos": len(self._current_state.completed_todos),
|
|
1415
|
-
"total_todos": len(self._current_state.todo_list),
|
|
1416
|
-
"current_task_index": self._current_state.current_task_index,
|
|
1417
1398
|
"is_complete": self._current_state.is_complete,
|
|
1418
|
-
"
|
|
1399
|
+
"browser_tasks_count": len(self._current_state.browser_tasks),
|
|
1400
|
+
"browser_results_count": len(self._current_state.browser_results)
|
|
1419
1401
|
}
|
|
1420
1402
|
|
|
1421
1403
|
return VibeSurfStatus(
|
|
@@ -1433,7 +1415,7 @@ class VibeSurfAgent:
|
|
|
1433
1415
|
progress={"error": str(e)}
|
|
1434
1416
|
)
|
|
1435
1417
|
|
|
1436
|
-
async def _stop_all_agents(self
|
|
1418
|
+
async def _stop_all_agents(self) -> None:
|
|
1437
1419
|
"""Stop all running agents"""
|
|
1438
1420
|
for agent_id, agent in self._running_agents.items():
|
|
1439
1421
|
try:
|
|
@@ -1444,36 +1426,120 @@ class VibeSurfAgent:
|
|
|
1444
1426
|
except Exception as e:
|
|
1445
1427
|
logger.warning(f"⚠️ Failed to stop agent {agent_id}: {e}")
|
|
1446
1428
|
|
|
1447
|
-
async def _pause_all_agents(self
|
|
1429
|
+
async def _pause_all_agents(self) -> None:
|
|
1448
1430
|
"""Pause all running agents"""
|
|
1449
1431
|
for agent_id, agent in self._running_agents.items():
|
|
1450
1432
|
try:
|
|
1451
1433
|
if hasattr(agent, 'pause'):
|
|
1452
1434
|
await agent.pause()
|
|
1453
1435
|
logger.info(f"⏸️ Paused agent {agent_id}")
|
|
1454
|
-
|
|
1455
|
-
self._current_state.paused_agents.add(agent_id)
|
|
1436
|
+
# Note: paused_agents removed in simplified state
|
|
1456
1437
|
except Exception as e:
|
|
1457
1438
|
logger.warning(f"⚠️ Failed to pause agent {agent_id}: {e}")
|
|
1458
1439
|
|
|
1459
|
-
async def _resume_all_agents(self
|
|
1440
|
+
async def _resume_all_agents(self) -> None:
|
|
1460
1441
|
"""Resume all paused agents"""
|
|
1461
1442
|
for agent_id, agent in self._running_agents.items():
|
|
1462
1443
|
try:
|
|
1463
1444
|
if hasattr(agent, 'resume'):
|
|
1464
1445
|
await agent.resume()
|
|
1465
1446
|
logger.info(f"▶️ Resumed agent {agent_id}")
|
|
1466
|
-
|
|
1467
|
-
self._current_state.paused_agents.discard(agent_id)
|
|
1447
|
+
# Note: paused_agents removed in simplified state
|
|
1468
1448
|
except Exception as e:
|
|
1469
1449
|
logger.warning(f"⚠️ Failed to resume agent {agent_id}: {e}")
|
|
1470
1450
|
|
|
1451
|
+
def _create_sub_agent_prompt(self, new_task: str, agent_id: str) -> str:
|
|
1452
|
+
"""
|
|
1453
|
+
Create a generic prompt for sub-agents when receiving new tasks.
|
|
1454
|
+
This prompt is designed to work with any type of sub-agent.
|
|
1455
|
+
"""
|
|
1456
|
+
return f"""🔄 **New Task/Guidance from User:**
|
|
1457
|
+
|
|
1458
|
+
{new_task}
|
|
1459
|
+
|
|
1460
|
+
**Note:** As a sub-agent, you should evaluate whether this new task is relevant to your current work. You may:
|
|
1461
|
+
- **Use it** if it provides helpful guidance, tips, or corrections for your current task
|
|
1462
|
+
- **Use it** if it's a follow-up task that enhances your current work
|
|
1463
|
+
- **Ignore it** if it's unrelated to your specific responsibilities or doesn't apply to your current task
|
|
1464
|
+
|
|
1465
|
+
Please continue with your assigned work, incorporating this guidance only if it's relevant and helpful to your specific role and current task."""
|
|
1466
|
+
|
|
1467
|
+
async def add_new_task(self, new_task: str) -> None:
|
|
1468
|
+
"""
|
|
1469
|
+
Add a new task or follow-up instruction during execution.
|
|
1470
|
+
This can be user feedback, guidance, or additional requirements.
|
|
1471
|
+
|
|
1472
|
+
Args:
|
|
1473
|
+
new_task: The new task, guidance, or instruction from the user
|
|
1474
|
+
"""
|
|
1475
|
+
activity_entry = {
|
|
1476
|
+
"agent_name": 'user',
|
|
1477
|
+
"agent_status": 'request', # working, result, error
|
|
1478
|
+
"agent_msg": f"{new_task}"
|
|
1479
|
+
}
|
|
1480
|
+
self.activity_logs.append(activity_entry)
|
|
1481
|
+
|
|
1482
|
+
# Create an English prompt for the main agent
|
|
1483
|
+
prompt = f"""🔄 **New Task/Follow-up from User:**
|
|
1484
|
+
|
|
1485
|
+
{new_task}
|
|
1486
|
+
|
|
1487
|
+
**Instructions:** This is additional guidance, a follow-up task, or user feedback to help with the current task execution. Please analyze how this relates to the current task and proceed accordingly."""
|
|
1488
|
+
|
|
1489
|
+
# Add to VibeSurf agent's message history
|
|
1490
|
+
self.message_history.append(UserMessage(content=prompt))
|
|
1491
|
+
logger.info(f"🌊 VibeSurf agent received new task: {new_task}")
|
|
1492
|
+
|
|
1493
|
+
# Propagate to all running sub-agents with generic sub-agent prompt
|
|
1494
|
+
if self._running_agents:
|
|
1495
|
+
logger.info(f"📡 Propagating new task to {len(self._running_agents)} running agents")
|
|
1496
|
+
for agent_id, agent in self._running_agents.items():
|
|
1497
|
+
try:
|
|
1498
|
+
if hasattr(agent, 'add_new_task'):
|
|
1499
|
+
# Use the generic sub-agent prompt
|
|
1500
|
+
sub_agent_prompt = self._create_sub_agent_prompt(new_task, agent_id)
|
|
1501
|
+
agent.add_new_task(sub_agent_prompt)
|
|
1502
|
+
logger.debug(f"✅ Sent new task to agent {agent_id}")
|
|
1503
|
+
else:
|
|
1504
|
+
logger.debug(f"⚠️ Agent {agent_id} doesn't support add_new_task")
|
|
1505
|
+
except Exception as e:
|
|
1506
|
+
logger.warning(f"⚠️ Failed to send new task to agent {agent_id}: {e}")
|
|
1507
|
+
else:
|
|
1508
|
+
logger.debug("📭 No running agents to propagate new task to")
|
|
1509
|
+
|
|
1510
|
+
async def process_upload_files(self, upload_files: Optional[List[str]] = None):
|
|
1511
|
+
if not upload_files:
|
|
1512
|
+
return []
|
|
1513
|
+
new_upload_files = []
|
|
1514
|
+
for ufile_path in upload_files:
|
|
1515
|
+
# timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
1516
|
+
dst_filename = f"upload_files/{os.path.basename(ufile_path)}"
|
|
1517
|
+
new_upload_files.append(dst_filename)
|
|
1518
|
+
return new_upload_files
|
|
1519
|
+
|
|
1520
|
+
def format_upload_files(self, upload_files: Optional[List[str]] = None, use_abspath: bool = False) -> str:
|
|
1521
|
+
"""Format uploaded file for LLM prompt"""
|
|
1522
|
+
if upload_files is None:
|
|
1523
|
+
return ""
|
|
1524
|
+
if use_abspath:
|
|
1525
|
+
file_urls = []
|
|
1526
|
+
for i, file_path in enumerate(upload_files):
|
|
1527
|
+
abs_file_path = self.file_system.get_absolute_path(file_path)
|
|
1528
|
+
normalized_path = abs_file_path.replace(os.path.sep, '/')
|
|
1529
|
+
file_url = f"{i + 1}. [{os.path.basename(file_path)}](file:///{normalized_path})"
|
|
1530
|
+
file_urls.append(file_url)
|
|
1531
|
+
return "\n".join(file_urls)
|
|
1532
|
+
else:
|
|
1533
|
+
return "\n".join(
|
|
1534
|
+
[f"{i + 1}. {file_path}" for i, file_path in enumerate(upload_files)])
|
|
1535
|
+
|
|
1471
1536
|
async def run(
|
|
1472
1537
|
self,
|
|
1473
1538
|
task: str,
|
|
1474
1539
|
upload_files: Optional[List[str]] = None,
|
|
1475
1540
|
session_id: Optional[str] = None,
|
|
1476
|
-
|
|
1541
|
+
thinking_mode: bool = True
|
|
1542
|
+
) -> str | None:
|
|
1477
1543
|
"""
|
|
1478
1544
|
Main execution method that returns markdown summary with control capabilities
|
|
1479
1545
|
|
|
@@ -1484,53 +1550,52 @@ class VibeSurfAgent:
|
|
|
1484
1550
|
Returns:
|
|
1485
1551
|
str: Markdown summary of execution results
|
|
1486
1552
|
"""
|
|
1487
|
-
logger.info(f"🚀 Starting VibeSurfAgent execution for task: {task
|
|
1488
|
-
agent_activity_logs = None
|
|
1553
|
+
logger.info(f"🚀 Starting VibeSurfAgent execution for task: {task}. Powered by LLM model: {self.llm.model_name}")
|
|
1489
1554
|
try:
|
|
1555
|
+
self.thinking_mode = thinking_mode
|
|
1490
1556
|
session_id = session_id or self.cur_session_id or uuid7str()
|
|
1491
1557
|
if session_id != self.cur_session_id:
|
|
1558
|
+
# Load session-specific data when switching sessions
|
|
1492
1559
|
self.cur_session_id = session_id
|
|
1560
|
+
self.message_history = self.load_message_history(session_id)
|
|
1561
|
+
self.activity_logs = self.load_activity_logs(session_id)
|
|
1562
|
+
session_dir = os.path.join(self.workspace_dir, "sessions", self.cur_session_id)
|
|
1563
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
1564
|
+
self.file_system = CustomFileSystem(session_dir)
|
|
1565
|
+
self.token_cost_service.clear_history()
|
|
1493
1566
|
|
|
1494
|
-
if self.cur_session_id not in self.message_history:
|
|
1495
|
-
logger.info(f"{self.cur_session_id} not found in message_history, create a new one.")
|
|
1496
|
-
self.message_history[self.cur_session_id] = []
|
|
1497
|
-
supervisor_message_history = self.message_history[self.cur_session_id]
|
|
1498
|
-
if not supervisor_message_history:
|
|
1499
|
-
supervisor_message_history.append(SystemMessage(content=SUPERVISOR_AGENT_SYSTEM_PROMPT))
|
|
1500
1567
|
if upload_files and not isinstance(upload_files, list):
|
|
1501
1568
|
upload_files = [upload_files]
|
|
1502
|
-
|
|
1569
|
+
upload_files = await self.process_upload_files(upload_files)
|
|
1570
|
+
|
|
1571
|
+
if not self.message_history:
|
|
1572
|
+
self.message_history.append(SystemMessage(content=VIBESURF_SYSTEM_PROMPT))
|
|
1573
|
+
|
|
1574
|
+
# Format processed upload files for prompt
|
|
1503
1575
|
user_request = f"* User's New Request:\n{task}\n"
|
|
1504
1576
|
if upload_files:
|
|
1577
|
+
upload_files_md = self.format_upload_files(upload_files)
|
|
1505
1578
|
user_request += f"* User Uploaded Files:\n{upload_files_md}\n"
|
|
1506
|
-
|
|
1507
|
-
UserMessage(
|
|
1508
|
-
content=user_request)
|
|
1579
|
+
self.message_history.append(
|
|
1580
|
+
UserMessage(content=user_request)
|
|
1509
1581
|
)
|
|
1510
1582
|
logger.info(user_request)
|
|
1511
1583
|
|
|
1512
|
-
|
|
1513
|
-
self.activity_logs[self.cur_session_id] = []
|
|
1514
|
-
agent_activity_logs = self.activity_logs[self.cur_session_id]
|
|
1584
|
+
abs_upload_files_md = self.format_upload_files(upload_files, use_abspath=True)
|
|
1515
1585
|
activity_entry = {
|
|
1516
1586
|
"agent_name": 'user',
|
|
1517
1587
|
"agent_status": 'request', # working, result, error
|
|
1518
|
-
"agent_msg": f"{task}\nUpload Files:\n{
|
|
1588
|
+
"agent_msg": f"{task}\nUpload Files:\n{abs_upload_files_md}\n" if upload_files else f"{task}"
|
|
1519
1589
|
}
|
|
1520
|
-
|
|
1590
|
+
self.activity_logs.append(activity_entry)
|
|
1521
1591
|
|
|
1522
|
-
# Initialize state
|
|
1592
|
+
# Initialize state first (needed for file processing)
|
|
1523
1593
|
initial_state = VibeSurfState(
|
|
1524
1594
|
original_task=task,
|
|
1525
1595
|
upload_files=upload_files or [],
|
|
1526
|
-
workspace_dir=self.workspace_dir,
|
|
1527
|
-
browser_manager=self.browser_manager,
|
|
1528
|
-
vibesurf_controller=self.controller,
|
|
1529
|
-
agent_activity_logs=agent_activity_logs,
|
|
1530
|
-
supervisor_message_history=supervisor_message_history,
|
|
1531
|
-
llm=self.llm,
|
|
1532
1596
|
session_id=session_id,
|
|
1533
|
-
|
|
1597
|
+
current_workspace_dir=os.path.join(self.workspace_dir, "sessions", self.cur_session_id),
|
|
1598
|
+
vibesurf_agent=self,
|
|
1534
1599
|
)
|
|
1535
1600
|
|
|
1536
1601
|
# Set current state for control operations
|
|
@@ -1565,36 +1630,39 @@ class VibeSurfAgent:
|
|
|
1565
1630
|
except asyncio.CancelledError:
|
|
1566
1631
|
logger.info("🛑 VibeSurfAgent execution was cancelled")
|
|
1567
1632
|
# Add cancellation activity log
|
|
1568
|
-
if
|
|
1633
|
+
if self.activity_logs:
|
|
1569
1634
|
activity_entry = {
|
|
1570
1635
|
"agent_name": "VibeSurfAgent",
|
|
1571
1636
|
"agent_status": "cancelled",
|
|
1572
1637
|
"agent_msg": "Task execution was cancelled by user request."
|
|
1573
1638
|
}
|
|
1574
|
-
|
|
1639
|
+
self.activity_logs.append(activity_entry)
|
|
1575
1640
|
return f"# Task Execution Cancelled\n\n**Task:** {task}\n\nExecution was stopped by user request."
|
|
1576
1641
|
except Exception as e:
|
|
1642
|
+
import traceback
|
|
1643
|
+
traceback.print_exc()
|
|
1577
1644
|
logger.error(f"❌ VibeSurfAgent execution failed: {e}")
|
|
1578
1645
|
# Add error activity log
|
|
1579
|
-
if
|
|
1646
|
+
if self.activity_logs:
|
|
1580
1647
|
activity_entry = {
|
|
1581
1648
|
"agent_name": "VibeSurfAgent",
|
|
1582
1649
|
"agent_status": "error",
|
|
1583
1650
|
"agent_msg": f"Task execution failed: {str(e)}"
|
|
1584
1651
|
}
|
|
1585
|
-
|
|
1652
|
+
self.activity_logs.append(activity_entry)
|
|
1586
1653
|
return f"# Task Execution Failed\n\n**Task:** {task}\n\n**Error:** {str(e)}\n\nPlease try again or contact support."
|
|
1587
1654
|
finally:
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
#
|
|
1596
|
-
self.
|
|
1597
|
-
|
|
1655
|
+
|
|
1656
|
+
activity_entry = {
|
|
1657
|
+
"agent_name": "VibeSurfAgent",
|
|
1658
|
+
"agent_status": "done", # working, result, error
|
|
1659
|
+
"agent_msg": "Finish Task."
|
|
1660
|
+
}
|
|
1661
|
+
self.activity_logs.append(activity_entry)
|
|
1662
|
+
# Save session-specific data
|
|
1663
|
+
if self.cur_session_id:
|
|
1664
|
+
self.save_message_history(self.cur_session_id)
|
|
1665
|
+
self.save_activity_logs(self.cur_session_id)
|
|
1598
1666
|
async with self._control_lock:
|
|
1599
1667
|
self._current_state = None
|
|
1600
1668
|
self._execution_task = None
|
|
@@ -1604,44 +1672,40 @@ class VibeSurfAgent:
|
|
|
1604
1672
|
List[Dict]]:
|
|
1605
1673
|
if session_id is None:
|
|
1606
1674
|
session_id = self.cur_session_id
|
|
1607
|
-
|
|
1608
|
-
logger.debug(f"📊 GET_ACTIVITY_LOGS DEBUG - Session: {session_id}, Message Index: {message_index}, Current Session: {self.cur_session_id}")
|
|
1609
|
-
|
|
1675
|
+
|
|
1610
1676
|
# Ensure session_id exists in activity_logs
|
|
1611
|
-
if session_id
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1677
|
+
if session_id != self.cur_session_id:
|
|
1678
|
+
session_logs = self.load_activity_logs(session_id)
|
|
1679
|
+
else:
|
|
1680
|
+
session_logs = self.activity_logs
|
|
1681
|
+
|
|
1616
1682
|
logger.debug(f"📋 Session {session_id} has {len(session_logs)} activity logs")
|
|
1617
|
-
|
|
1683
|
+
|
|
1618
1684
|
if message_index is None:
|
|
1619
1685
|
logger.debug(f"📤 Returning all {len(session_logs)} activity logs for session {session_id}")
|
|
1620
1686
|
return session_logs
|
|
1621
1687
|
else:
|
|
1622
1688
|
if message_index >= len(session_logs):
|
|
1623
|
-
logger.debug(
|
|
1689
|
+
logger.debug(
|
|
1690
|
+
f"⚠️ Message index {message_index} out of range for session {session_id} (max index: {len(session_logs) - 1})")
|
|
1624
1691
|
return None
|
|
1625
1692
|
else:
|
|
1626
1693
|
activity_log = session_logs[message_index]
|
|
1627
|
-
logger.debug(
|
|
1694
|
+
logger.debug(
|
|
1695
|
+
f"📤 Returning activity log at index {message_index}: {activity_log.get('agent_name', 'unknown')} - {activity_log.get('agent_status', 'unknown')}")
|
|
1628
1696
|
return activity_log
|
|
1629
1697
|
|
|
1630
1698
|
async def _get_result(self, state) -> str:
|
|
1631
1699
|
"""Get the final result from execution with simplified workflow support"""
|
|
1632
1700
|
# Handle both dict and dataclass state types due to LangGraph serialization
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
final_summary = state.get('final_summary') if isinstance(state, dict) else getattr(state, 'final_summary', None)
|
|
1701
|
+
final_response = state.get('final_response') if isinstance(state, dict) else getattr(state, 'final_response',
|
|
1702
|
+
None)
|
|
1636
1703
|
original_task = state.get('original_task') if isinstance(state, dict) else getattr(state, 'original_task',
|
|
1637
1704
|
'Unknown task')
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
if simple_response:
|
|
1641
|
-
return f"# Response\n\n{simple_response}"
|
|
1642
|
-
elif final_summary:
|
|
1643
|
-
return final_summary
|
|
1705
|
+
if final_response:
|
|
1706
|
+
return final_response
|
|
1644
1707
|
else:
|
|
1645
1708
|
return f"# Task Execution Completed\n\n**Task:** {original_task}\n\nTask execution completed but no detailed result available."
|
|
1646
1709
|
|
|
1710
|
+
|
|
1647
1711
|
workflow = create_vibe_surf_workflow()
|