vibesurf 0.1.10__py3-none-any.whl → 0.1.12__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 +880 -825
- vibe_surf/agents/views.py +130 -0
- vibe_surf/backend/api/activity.py +3 -1
- vibe_surf/backend/api/browser.py +9 -5
- 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 +46 -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 +4 -2
- vibe_surf/backend/shared_state.py +28 -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 +0 -4
- vibe_surf/browser/browser_manager.py +14 -8
- vibe_surf/browser/utils.py +5 -3
- vibe_surf/browser/watchdogs/dom_watchdog.py +0 -45
- vibe_surf/chrome_extension/background.js +4 -0
- vibe_surf/chrome_extension/scripts/api-client.js +13 -0
- vibe_surf/chrome_extension/scripts/file-manager.js +27 -71
- vibe_surf/chrome_extension/scripts/session-manager.js +21 -3
- vibe_surf/chrome_extension/scripts/ui-manager.js +831 -48
- vibe_surf/chrome_extension/sidepanel.html +21 -4
- vibe_surf/chrome_extension/styles/activity.css +365 -5
- vibe_surf/chrome_extension/styles/input.css +139 -0
- vibe_surf/cli.py +5 -22
- vibe_surf/common.py +35 -0
- vibe_surf/llm/openai_compatible.py +217 -99
- vibe_surf/logger.py +99 -0
- vibe_surf/{controller/vibesurf_tools.py → tools/browser_use_tools.py} +233 -219
- vibe_surf/tools/file_system.py +437 -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.10.dist-info → vibesurf-0.1.12.dist-info}/METADATA +6 -2
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/RECORD +49 -43
- vibe_surf/controller/file_system.py +0 -53
- vibe_surf/controller/views.py +0 -37
- /vibe_surf/{controller → tools}/__init__.py +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.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,184 +337,164 @@ 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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
+
context_info.append(f"Current Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
367
|
+
if browser_tabs:
|
|
368
|
+
browser_tabs_info = {}
|
|
369
|
+
for tab in browser_tabs:
|
|
370
|
+
browser_tabs_info[tab.target_id[-4:]] = {
|
|
371
|
+
"page_title": tab.title,
|
|
372
|
+
"page_url": tab.url,
|
|
373
|
+
}
|
|
374
|
+
context_info.append(
|
|
375
|
+
f"Current Available Browser Tabs:\n{json.dumps(browser_tabs_info, ensure_ascii=False, indent=2)}\n")
|
|
478
376
|
if active_browser_tab:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
377
|
+
context_info.append(f"Current Active Browser Tab:{active_browser_tab.target_id[-4:]}\n")
|
|
378
|
+
if state.browser_results:
|
|
379
|
+
results_md = format_browser_results(state.browser_results)
|
|
380
|
+
context_info.append(f"Previous Browser Results:\n{results_md}\n")
|
|
381
|
+
if state.generated_report_result:
|
|
382
|
+
if state.generated_report_result.success:
|
|
383
|
+
context_info.append(f"Generated Report: ✅ Success - {state.generated_report_result.report_path}\n")
|
|
384
|
+
else:
|
|
385
|
+
context_info.append(f"Generated Report: ❌ Failed - {state.generated_report_result.msg}\nPath: {state.generated_report_result.report_path}\n")
|
|
386
|
+
|
|
387
|
+
context_str = "\n".join(context_info) if context_info else "No additional context available."
|
|
388
|
+
vibesurf_agent.message_history.append(UserMessage(content=context_str))
|
|
482
389
|
|
|
483
|
-
# Reset prev_browser_results
|
|
484
|
-
state.prev_browser_results = []
|
|
485
390
|
try:
|
|
486
|
-
response
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
# Log
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
#
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
log_agent_activity(state, "supervisor_agent", "result", f"Todo List:\n\n{todo_todo_list_md}")
|
|
511
|
-
# Continue in supervisor to assign tasks
|
|
512
|
-
state.current_step = "supervisor_agent"
|
|
513
|
-
|
|
514
|
-
elif action == "update_todos":
|
|
515
|
-
# Replace all remaining todos with the new list
|
|
516
|
-
todo_items = supervisor_result.get("todo_items", [])
|
|
517
|
-
if todo_items:
|
|
518
|
-
# Clear current todo list and replace with new items
|
|
519
|
-
state.todo_list = [TodoItem(task=task) for task in todo_items]
|
|
520
|
-
todo_todo_list_md = format_todo_list_markdown(state.completed_todos + state.todo_list)
|
|
521
|
-
supervisor_message_history.append(
|
|
522
|
-
UserMessage(content=f"Successfully Updated todo list:\n{todo_todo_list_md}"))
|
|
523
|
-
log_agent_activity(state, "supervisor_agent", "result", f"Todo List:\n\n{todo_todo_list_md}")
|
|
524
|
-
else:
|
|
525
|
-
# If no todo_items provided, clear the list
|
|
526
|
-
state.todo_list = []
|
|
527
|
-
todo_todo_list_md = format_todo_list_markdown(state.completed_todos + state.todo_list)
|
|
528
|
-
supervisor_message_history.append(
|
|
529
|
-
UserMessage(content=f"Cleared todo list - all tasks completed:\n{todo_todo_list_md}"))
|
|
530
|
-
log_agent_activity(state, "supervisor_agent", "result",
|
|
531
|
-
f"Cleared todo list - all tasks completed\n{todo_todo_list_md}")
|
|
532
|
-
|
|
533
|
-
# Continue in supervisor to assign tasks
|
|
534
|
-
state.current_step = "supervisor_agent"
|
|
535
|
-
|
|
536
|
-
elif action == "assign_browser_task":
|
|
537
|
-
# Assign browser tasks
|
|
538
|
-
task_type = supervisor_result.get("task_type", "single")
|
|
539
|
-
tasks_to_execute = supervisor_result.get("tasks_to_execute", [])
|
|
540
|
-
|
|
541
|
-
if tasks_to_execute:
|
|
542
|
-
tasks_to_execute_new = []
|
|
543
|
-
todo_indices = [] # Track which todo items are being executed
|
|
544
|
-
for task_item in tasks_to_execute:
|
|
545
|
-
if isinstance(task_item, list):
|
|
546
|
-
# Format: [page_index, todo_index]
|
|
547
|
-
page_index, todo_index = task_item
|
|
548
|
-
if isinstance(todo_index, int):
|
|
549
|
-
if todo_index < len(state.todo_list):
|
|
550
|
-
task_description = state.todo_list[todo_index].task
|
|
551
|
-
tasks_to_execute_new.append([browser_tabs[page_index].target_id, task_description])
|
|
552
|
-
todo_indices.append(todo_index)
|
|
553
|
-
elif isinstance(todo_index, str):
|
|
554
|
-
tasks_to_execute_new.append([browser_tabs[page_index].target_id, todo_index])
|
|
555
|
-
else:
|
|
556
|
-
# Format: todo_index
|
|
557
|
-
todo_index = task_item
|
|
558
|
-
if isinstance(todo_index, int):
|
|
559
|
-
if todo_index < len(state.todo_list):
|
|
560
|
-
task_description = state.todo_list[todo_index].task
|
|
561
|
-
tasks_to_execute_new.append(task_description)
|
|
562
|
-
todo_indices.append(todo_index)
|
|
563
|
-
elif isinstance(todo_index, str):
|
|
564
|
-
tasks_to_execute_new.append(todo_index)
|
|
565
|
-
logger.info(f"tasks_to_execute: {tasks_to_execute}")
|
|
566
|
-
state.execution_mode = ExecutionMode(
|
|
567
|
-
mode=task_type,
|
|
568
|
-
reason=reasoning
|
|
569
|
-
)
|
|
570
|
-
state.pending_tasks = tasks_to_execute_new
|
|
571
|
-
state.pending_todo_indices = todo_indices # Store which todo indices are being executed
|
|
391
|
+
# Get LLM response with action output format
|
|
392
|
+
response = await vibesurf_agent.llm.ainvoke(vibesurf_agent.message_history, output_format=AgentOutput)
|
|
393
|
+
parsed = response.completion
|
|
394
|
+
actions = parsed.action
|
|
395
|
+
vibesurf_agent.message_history.append(
|
|
396
|
+
AssistantMessage(content=json.dumps(response.completion.model_dump(exclude_none=True, exclude_unset=True),
|
|
397
|
+
ensure_ascii=False)))
|
|
398
|
+
|
|
399
|
+
# Log thinking if present
|
|
400
|
+
if hasattr(parsed, 'thinking') and parsed.thinking:
|
|
401
|
+
await log_agent_activity(state, agent_name, "thinking", parsed.thinking)
|
|
402
|
+
|
|
403
|
+
for i, action in enumerate(actions):
|
|
404
|
+
action_data = action.model_dump(exclude_unset=True)
|
|
405
|
+
action_name = next(iter(action_data.keys())) if action_data else 'unknown'
|
|
406
|
+
logger.info(f"🛠️ Processing VibeSurf action {i + 1}/{len(actions)}: {action_name}")
|
|
407
|
+
|
|
408
|
+
# Check for special routing actions
|
|
409
|
+
if action_name == 'execute_browser_use_agent':
|
|
410
|
+
# Route to browser task execution node
|
|
411
|
+
params = action_data[action_name]
|
|
412
|
+
state.browser_tasks = params.get('tasks', [])
|
|
413
|
+
state.current_action = 'execute_browser_use_agent'
|
|
414
|
+
state.action_params = params
|
|
572
415
|
state.current_step = "browser_task_execution"
|
|
573
416
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
#
|
|
599
|
-
|
|
417
|
+
# Log agent activity
|
|
418
|
+
browser_tasks_md = []
|
|
419
|
+
for browser_task in state.browser_tasks:
|
|
420
|
+
bu_task = browser_task.get('task', "")
|
|
421
|
+
if bu_task:
|
|
422
|
+
browser_tasks_md.append(f"- [ ] {bu_task}")
|
|
423
|
+
browser_tasks_md = '\n'.join(browser_tasks_md)
|
|
424
|
+
agent_msg = f"Routing to browser task execution with {len(state.browser_tasks)} browser tasks:\n\n{browser_tasks_md}"
|
|
425
|
+
await log_agent_activity(state, agent_name, "working", agent_msg)
|
|
426
|
+
logger.debug(agent_msg)
|
|
427
|
+
return state
|
|
428
|
+
|
|
429
|
+
elif action_name == 'execute_report_writer_agent':
|
|
430
|
+
# Route to report task execution node
|
|
431
|
+
params = action_data[action_name]
|
|
432
|
+
state.current_action = 'execute_report_writer_agent'
|
|
433
|
+
state.action_params = params
|
|
434
|
+
state.current_step = "report_task_execution"
|
|
435
|
+
report_task = params.get('task', "")
|
|
436
|
+
agent_msg = f"Routing to report generation with task:\n{report_task}"
|
|
437
|
+
await log_agent_activity(state, agent_name, "working", agent_msg)
|
|
438
|
+
return state
|
|
439
|
+
|
|
440
|
+
elif action_name == 'task_done':
|
|
441
|
+
# Handle response/completion - direct to END
|
|
442
|
+
params = action_data[action_name]
|
|
443
|
+
response_content = params.get('response', 'Task completed!')
|
|
444
|
+
follow_tasks = params.get('suggestion_follow_tasks', [])
|
|
445
|
+
state.current_step = "END"
|
|
446
|
+
|
|
447
|
+
# Format final response
|
|
448
|
+
final_response = f"{response_content}"
|
|
449
|
+
await log_agent_activity(state, agent_name, "result", final_response)
|
|
450
|
+
|
|
451
|
+
if follow_tasks:
|
|
452
|
+
await log_agent_activity(state, agent_name, "suggestion_tasks",
|
|
453
|
+
'\n'.join(follow_tasks))
|
|
454
|
+
final_response += "\n\n## Suggested Follow-up Tasks:\n"
|
|
455
|
+
for j, task in enumerate(follow_tasks[:3], 1):
|
|
456
|
+
final_response += f"{j}. {task}\n"
|
|
457
|
+
|
|
458
|
+
state.final_response = final_response
|
|
459
|
+
logger.debug(final_response)
|
|
600
460
|
state.is_complete = True
|
|
601
|
-
state
|
|
602
|
-
|
|
461
|
+
return state
|
|
462
|
+
|
|
603
463
|
else:
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
464
|
+
if "todos" in action_name:
|
|
465
|
+
todo_content = await vibesurf_agent.file_system.read_file('todo.md')
|
|
466
|
+
action_msg = f"{action_name}:\n\n{todo_content}"
|
|
467
|
+
logger.debug(action_msg)
|
|
468
|
+
await log_agent_activity(state, agent_name, "working", action_msg)
|
|
469
|
+
else:
|
|
470
|
+
action_msg = f"**⚡ Actions:**\n"
|
|
471
|
+
action_msg += f"```json\n{json.dumps(action_data, indent=2, ensure_ascii=False)}\n```"
|
|
472
|
+
logger.debug(action_msg)
|
|
473
|
+
await log_agent_activity(state, agent_name, "working", action_msg)
|
|
474
|
+
|
|
475
|
+
result = await vibesurf_agent.tools.act(
|
|
476
|
+
action=action,
|
|
477
|
+
browser_manager=vibesurf_agent.browser_manager,
|
|
478
|
+
llm=vibesurf_agent.llm,
|
|
479
|
+
file_system=vibesurf_agent.file_system,
|
|
480
|
+
)
|
|
481
|
+
state.current_step = "vibesurf_agent"
|
|
482
|
+
if result.extracted_content:
|
|
483
|
+
vibesurf_agent.message_history.append(
|
|
484
|
+
UserMessage(content=f'Action result:\n{result.extracted_content}'))
|
|
485
|
+
await log_agent_activity(state, agent_name, "result", result.extracted_content)
|
|
486
|
+
|
|
487
|
+
if result.error:
|
|
488
|
+
vibesurf_agent.message_history.append(UserMessage(content=f'Action error:\n{result.error}'))
|
|
489
|
+
await log_agent_activity(state, agent_name, "error", result.error)
|
|
612
490
|
|
|
613
491
|
return state
|
|
614
492
|
|
|
615
493
|
except Exception as e:
|
|
616
|
-
logger.error(f"❌
|
|
617
|
-
state.
|
|
618
|
-
|
|
494
|
+
logger.error(f"❌ VibeSurf agent failed: {e}")
|
|
495
|
+
state.final_response = f"Task execution failed: {str(e)}"
|
|
496
|
+
state.is_complete = True
|
|
497
|
+
await log_agent_activity(state, agent_name, "error", f"Agent failed: {str(e)}")
|
|
619
498
|
return state
|
|
620
499
|
|
|
621
500
|
|
|
@@ -627,187 +506,132 @@ async def browser_task_execution_node(state: VibeSurfState) -> VibeSurfState:
|
|
|
627
506
|
|
|
628
507
|
|
|
629
508
|
async def _browser_task_execution_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
630
|
-
"""Implementation of browser task execution node"""
|
|
631
|
-
logger.info("🚀 Executing browser tasks assigned by
|
|
632
|
-
|
|
633
|
-
# Log agent activity
|
|
634
|
-
log_agent_activity(state, "browser_task_executor", "working",
|
|
635
|
-
f"Executing {len(state.pending_tasks)} browser tasks in {state.execution_mode.mode if state.execution_mode else 'single'} mode")
|
|
636
|
-
|
|
637
|
-
# Setup file organization
|
|
638
|
-
ensure_directories(state)
|
|
639
|
-
|
|
509
|
+
"""Implementation of browser task execution node - simplified tab-based approach"""
|
|
510
|
+
logger.info("🚀 Executing browser tasks assigned by vibesurf agent...")
|
|
640
511
|
try:
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
512
|
+
task_count = len(state.browser_tasks)
|
|
513
|
+
if task_count == 0:
|
|
514
|
+
raise ValueError("No browser tasks assigned. Please assign 1 task at least.")
|
|
515
|
+
|
|
516
|
+
if task_count <= 1:
|
|
517
|
+
# Single task execution
|
|
518
|
+
logger.info("📝 Using single execution for single task")
|
|
519
|
+
result = await execute_single_browser_tasks(state)
|
|
520
|
+
results = [result]
|
|
521
|
+
# Update browser results
|
|
522
|
+
state.browser_results.extend(results)
|
|
644
523
|
else:
|
|
645
|
-
#
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
for i, todo_index in enumerate(state.pending_todo_indices):
|
|
654
|
-
if todo_index < len(state.todo_list) and state.todo_list[todo_index].status != "completed":
|
|
655
|
-
todo = state.todo_list[todo_index]
|
|
656
|
-
todo.status = "completed"
|
|
657
|
-
if i < len(results):
|
|
658
|
-
todo.result = results[i].result if results[i].success else None
|
|
659
|
-
todo.error = results[i].error if not results[i].success else None
|
|
660
|
-
state.completed_todos.append(todo)
|
|
661
|
-
|
|
662
|
-
# Remove completed todos from the todo list
|
|
663
|
-
# Sort indices in reverse order to avoid index shifting issues
|
|
664
|
-
for todo_index in sorted(state.pending_todo_indices, reverse=True):
|
|
665
|
-
if todo_index < len(state.todo_list):
|
|
666
|
-
state.todo_list.pop(todo_index)
|
|
667
|
-
|
|
668
|
-
# Clear pending tasks and indices
|
|
669
|
-
state.pending_tasks = []
|
|
670
|
-
state.pending_todo_indices = []
|
|
671
|
-
|
|
672
|
-
# Return to supervisor for next decision
|
|
673
|
-
state.current_step = "supervisor_agent"
|
|
524
|
+
# Multiple tasks execution - parallel approach
|
|
525
|
+
logger.info(f"🚀 Using parallel execution for {task_count} tasks")
|
|
526
|
+
results = await execute_parallel_browser_tasks(state)
|
|
527
|
+
# Update browser results
|
|
528
|
+
state.browser_results.extend(results)
|
|
529
|
+
|
|
530
|
+
# Return to vibesurf agent for next decision
|
|
531
|
+
state.current_step = "vibesurf_agent"
|
|
674
532
|
|
|
675
533
|
# Log result
|
|
676
534
|
successful_tasks = sum(1 for result in results if result.success)
|
|
677
|
-
log_agent_activity(state, "browser_task_executor", "result",
|
|
678
|
-
|
|
535
|
+
await log_agent_activity(state, "browser_task_executor", "result",
|
|
536
|
+
f"Browser execution completed: {successful_tasks}/{len(results)} tasks successful")
|
|
679
537
|
|
|
680
538
|
logger.info(f"✅ Browser task execution completed with {len(results)} results")
|
|
681
539
|
return state
|
|
682
540
|
|
|
683
541
|
except Exception as e:
|
|
684
542
|
logger.error(f"❌ Browser task execution failed: {e}")
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
task_description = task
|
|
694
|
-
error_results.append(BrowserTaskResult(
|
|
695
|
-
agent_id="error",
|
|
696
|
-
task=task_description,
|
|
697
|
-
success=False,
|
|
698
|
-
error=str(e)
|
|
699
|
-
))
|
|
700
|
-
|
|
701
|
-
state.browser_results.extend(error_results)
|
|
702
|
-
state.pending_tasks = []
|
|
703
|
-
state.pending_todo_indices = []
|
|
704
|
-
state.current_step = "supervisor_agent"
|
|
705
|
-
|
|
706
|
-
log_agent_activity(state, "browser_task_executor", "error", f"Browser execution failed: {str(e)}")
|
|
707
|
-
return state
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
async def report_task_execution_node(state: VibeSurfState) -> VibeSurfState:
|
|
711
|
-
"""
|
|
712
|
-
Execute HTML report generation task assigned by supervisor agent
|
|
713
|
-
"""
|
|
714
|
-
return await control_aware_node(_report_task_execution_node_impl, state, "report_task_execution")
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
async def _report_task_execution_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
718
|
-
"""Implementation of report task execution node"""
|
|
719
|
-
logger.info("📄 Executing HTML report generation task...")
|
|
720
|
-
|
|
721
|
-
# Log agent activity
|
|
722
|
-
log_agent_activity(state, "report_task_executor", "working", "Generating HTML report")
|
|
723
|
-
|
|
724
|
-
try:
|
|
725
|
-
# Use ReportWriterAgent to generate HTML report
|
|
726
|
-
report_writer = ReportWriterAgent(
|
|
727
|
-
llm=state.llm,
|
|
728
|
-
workspace_dir=state.task_dir
|
|
543
|
+
import traceback
|
|
544
|
+
traceback.print_exc()
|
|
545
|
+
state.browser_results.append(BrowserTaskResult(
|
|
546
|
+
agent_id="unknown",
|
|
547
|
+
agent_workdir="unknown",
|
|
548
|
+
task='unknown',
|
|
549
|
+
success=False,
|
|
550
|
+
error=str(e)
|
|
729
551
|
)
|
|
552
|
+
)
|
|
553
|
+
state.current_step = "vibesurf_agent"
|
|
730
554
|
|
|
731
|
-
|
|
732
|
-
"original_task": state.original_task,
|
|
733
|
-
"execution_results": state.browser_results,
|
|
734
|
-
"report_type": "detailed", # Default to detailed report
|
|
735
|
-
"upload_files": state.upload_files
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
report_path = await report_writer.generate_report(report_data)
|
|
739
|
-
|
|
740
|
-
state.generated_report_path = report_path
|
|
741
|
-
|
|
742
|
-
# Return to supervisor for next decision
|
|
743
|
-
state.current_step = "supervisor_agent"
|
|
744
|
-
|
|
745
|
-
log_agent_activity(state, "report_task_executor", "result",
|
|
746
|
-
f"HTML report generated successfully at: `{report_path}`")
|
|
747
|
-
|
|
748
|
-
logger.info(f"✅ Report generated: {report_path}")
|
|
749
|
-
return state
|
|
750
|
-
|
|
751
|
-
except Exception as e:
|
|
752
|
-
logger.error(f"❌ Report generation failed: {e}")
|
|
753
|
-
state.current_step = "supervisor_agent"
|
|
754
|
-
log_agent_activity(state, "report_task_executor", "error", f"Report generation failed: {str(e)}")
|
|
555
|
+
await log_agent_activity(state, "browser_task_executor", "error", f"Browser execution failed: {str(e)}")
|
|
755
556
|
return state
|
|
756
557
|
|
|
757
558
|
|
|
758
|
-
async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTaskResult]:
|
|
559
|
+
async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTaskResult] | None:
|
|
759
560
|
"""Execute pending tasks in parallel using multiple browser agents"""
|
|
760
561
|
logger.info("🔄 Executing pending tasks in parallel...")
|
|
761
562
|
|
|
762
563
|
# Register agents with browser manager
|
|
763
564
|
agents = []
|
|
764
|
-
pending_tasks = state.
|
|
565
|
+
pending_tasks = state.browser_tasks
|
|
765
566
|
bu_agent_ids = []
|
|
766
567
|
register_sessions = []
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
568
|
+
task_id = nanoid.generate(size=5)
|
|
569
|
+
bu_agents_workdir = state.vibesurf_agent.file_system.get_dir() / "bu_agents"
|
|
570
|
+
bu_agents_workdir.mkdir(parents=True, exist_ok=True)
|
|
571
|
+
|
|
572
|
+
for i, task_info in enumerate(pending_tasks):
|
|
573
|
+
agent_id = f"bu_agent-{task_id}-{i + 1:03d}"
|
|
574
|
+
task_description = task_info.get('task', '')
|
|
575
|
+
if not task_description:
|
|
576
|
+
continue
|
|
577
|
+
target_id = task_info.get('target_id', None)
|
|
774
578
|
register_sessions.append(
|
|
775
|
-
state.browser_manager.register_agent(agent_id, target_id=target_id)
|
|
579
|
+
state.vibesurf_agent.browser_manager.register_agent(agent_id, target_id=target_id)
|
|
776
580
|
)
|
|
777
581
|
bu_agent_ids.append(agent_id)
|
|
778
582
|
agent_browser_sessions = await asyncio.gather(*register_sessions)
|
|
779
583
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
584
|
+
vibesurf_tools = state.vibesurf_agent.tools
|
|
585
|
+
bu_tools = BrowserUseTools()
|
|
586
|
+
for mcp_server_name, mcp_client in vibesurf_tools.mcp_clients.items():
|
|
587
|
+
await mcp_client.register_to_tools(
|
|
588
|
+
tools=bu_tools,
|
|
589
|
+
prefix=f"mcp.{mcp_server_name}."
|
|
590
|
+
)
|
|
591
|
+
bu_tasks = [None] * len(pending_tasks)
|
|
592
|
+
for i, task_info in enumerate(pending_tasks):
|
|
593
|
+
agent_id = f"bu_agent-{task_id}-{i + 1:03d}"
|
|
594
|
+
task_description = task_info.get('task', '')
|
|
595
|
+
if not task_description:
|
|
596
|
+
continue
|
|
597
|
+
task_files = task_info.get('task_files', [])
|
|
598
|
+
bu_agent_workdir = bu_agents_workdir / f"{task_id}-{i + 1:03d}"
|
|
599
|
+
bu_agent_workdir.mkdir(parents=True, exist_ok=True)
|
|
600
|
+
agent_name = f"browser_use_agent-{task_id}-{i + 1:03d}"
|
|
601
|
+
# Log agent creation
|
|
602
|
+
await log_agent_activity(state, agent_name, "working", f"{task_description}")
|
|
603
|
+
|
|
786
604
|
try:
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
605
|
+
available_file_paths = []
|
|
606
|
+
if task_files:
|
|
607
|
+
for task_file in task_files:
|
|
608
|
+
upload_workdir = bu_agent_workdir / "upload_files"
|
|
609
|
+
upload_workdir.mkdir(parents=True, exist_ok=True)
|
|
610
|
+
task_file_path = state.vibesurf_agent.file_system.get_absolute_path(task_file)
|
|
611
|
+
if os.path.exists(task_file_path):
|
|
612
|
+
logger.info(f"Copy {task_file_path} to {upload_workdir}")
|
|
613
|
+
shutil.copy(task_file_path, str(upload_workdir))
|
|
614
|
+
available_file_paths.append(os.path.join("upload_files", os.path.basename(task_file_path)))
|
|
790
615
|
|
|
791
616
|
# Create BrowserUseAgent for each task
|
|
792
|
-
if
|
|
793
|
-
upload_files_md =
|
|
794
|
-
bu_task = task_description + f"\
|
|
617
|
+
if available_file_paths:
|
|
618
|
+
upload_files_md = '\n'.join(available_file_paths)
|
|
619
|
+
bu_task = task_description + f"\nNecessary files for this task:\n{upload_files_md}\n"
|
|
795
620
|
else:
|
|
796
621
|
bu_task = task_description
|
|
797
|
-
|
|
622
|
+
bu_tasks[i] = bu_task
|
|
798
623
|
# Create step callback for this agent
|
|
799
|
-
agent_name = f"browser_use_agent-{i + 1}-{state.task_id[-4:]}"
|
|
800
624
|
step_callback = create_browser_agent_step_callback(state, agent_name)
|
|
801
|
-
|
|
802
625
|
agent = BrowserUseAgent(
|
|
803
626
|
task=bu_task,
|
|
804
|
-
llm=state.llm,
|
|
627
|
+
llm=state.vibesurf_agent.llm,
|
|
805
628
|
browser_session=agent_browser_sessions[i],
|
|
806
|
-
|
|
807
|
-
task_id=f"{
|
|
808
|
-
file_system_path=
|
|
629
|
+
tools=bu_tools,
|
|
630
|
+
task_id=f"{task_id}-{i + 1:03d}",
|
|
631
|
+
file_system_path=str(bu_agent_workdir),
|
|
809
632
|
register_new_step_callback=step_callback,
|
|
810
|
-
extend_system_message=
|
|
633
|
+
extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
|
|
634
|
+
token_cost_service=state.vibesurf_agent.token_cost_service
|
|
811
635
|
)
|
|
812
636
|
agents.append(agent)
|
|
813
637
|
|
|
@@ -817,9 +641,11 @@ async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTa
|
|
|
817
641
|
logger.debug(f"🔗 Registered parallel agent {agent_id} for control coordination")
|
|
818
642
|
|
|
819
643
|
except Exception as e:
|
|
644
|
+
import traceback
|
|
645
|
+
traceback.print_exc()
|
|
820
646
|
logger.error(f"❌ Failed to create agent {agent_id}: {e}")
|
|
821
|
-
log_agent_activity(state,
|
|
822
|
-
|
|
647
|
+
await log_agent_activity(state, agent_name, "error",
|
|
648
|
+
f"Failed to create agent: {str(e)}")
|
|
823
649
|
|
|
824
650
|
# Execute all agents in parallel
|
|
825
651
|
try:
|
|
@@ -828,155 +654,284 @@ async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTa
|
|
|
828
654
|
# Process results
|
|
829
655
|
results = []
|
|
830
656
|
for i, (agent, history) in enumerate(zip(agents, histories)):
|
|
831
|
-
|
|
657
|
+
task = bu_tasks[i]
|
|
658
|
+
bu_agent_workdir = f"bu_agents/{task_id}-{i + 1:03d}"
|
|
659
|
+
agent_name = f"browser_use_agent-{task_id}-{i + 1:03d}"
|
|
660
|
+
|
|
661
|
+
important_files = []
|
|
662
|
+
if history and history.history and len(history.history[-1].result) > 0:
|
|
663
|
+
last_result = history.history[-1].result[-1]
|
|
664
|
+
important_files = last_result.attachments
|
|
665
|
+
if important_files:
|
|
666
|
+
important_files = [os.path.join(bu_agent_workdir, file_name) for file_name in important_files]
|
|
667
|
+
|
|
832
668
|
if isinstance(history, Exception):
|
|
833
669
|
results.append(BrowserTaskResult(
|
|
834
|
-
agent_id=f"
|
|
835
|
-
task=
|
|
670
|
+
agent_id=f"{task_id}-{i + 1:03d}",
|
|
671
|
+
task=task,
|
|
836
672
|
success=False,
|
|
837
|
-
error=str(history)
|
|
673
|
+
error=str(history),
|
|
674
|
+
agent_workdir=bu_agent_workdir,
|
|
675
|
+
important_files=important_files,
|
|
838
676
|
))
|
|
839
677
|
# Log error
|
|
840
|
-
log_agent_activity(state, f"
|
|
841
|
-
f"Task failed: {str(history)}")
|
|
678
|
+
await log_agent_activity(state, agent_name, "error", f"Task failed: {str(history)}")
|
|
842
679
|
else:
|
|
843
680
|
results.append(BrowserTaskResult(
|
|
844
|
-
agent_id=f"
|
|
845
|
-
task=
|
|
681
|
+
agent_id=f"{task_id}-{i + 1:03d}",
|
|
682
|
+
task=task,
|
|
683
|
+
agent_workdir=bu_agent_workdir,
|
|
846
684
|
success=history.is_successful(),
|
|
685
|
+
important_files=important_files,
|
|
847
686
|
result=history.final_result() if hasattr(history, 'final_result') else "Task completed",
|
|
848
687
|
error=str(history.errors()) if history.has_errors() and not history.is_successful() else ""
|
|
849
688
|
))
|
|
850
689
|
# Log result
|
|
851
690
|
if history.is_successful():
|
|
852
|
-
result_text = history.final_result()
|
|
853
|
-
log_agent_activity(state,
|
|
854
|
-
|
|
691
|
+
result_text = history.final_result()
|
|
692
|
+
await log_agent_activity(state, agent_name, "result",
|
|
693
|
+
f"Task completed successfully: \n{result_text}")
|
|
855
694
|
else:
|
|
856
|
-
error_text = str(history.errors())
|
|
857
|
-
log_agent_activity(state, f"
|
|
858
|
-
f"Task failed: {error_text}")
|
|
695
|
+
error_text = str(history.errors())
|
|
696
|
+
await log_agent_activity(state, agent_name, "error", f"Task failed: {error_text}")
|
|
859
697
|
|
|
860
698
|
return results
|
|
861
699
|
|
|
700
|
+
except Exception as e:
|
|
701
|
+
import traceback
|
|
702
|
+
traceback.print_exc()
|
|
703
|
+
|
|
862
704
|
finally:
|
|
863
705
|
# Remove agents from control tracking and cleanup browser sessions
|
|
864
706
|
for i, agent_id in enumerate(bu_agent_ids):
|
|
865
707
|
if not isinstance(pending_tasks[i], list):
|
|
866
|
-
await state.browser_manager.unregister_agent(agent_id, close_tabs=True)
|
|
708
|
+
await state.vibesurf_agent.browser_manager.unregister_agent(agent_id, close_tabs=True)
|
|
867
709
|
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
868
710
|
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
869
711
|
logger.debug(f"🔗 Unregistered parallel agent {agent_id} from control coordination")
|
|
870
712
|
|
|
871
713
|
|
|
872
|
-
async def execute_single_browser_tasks(state: VibeSurfState) ->
|
|
714
|
+
async def execute_single_browser_tasks(state: VibeSurfState) -> BrowserTaskResult | None:
|
|
873
715
|
"""Execute pending tasks in single mode one by one"""
|
|
874
716
|
logger.info("🔄 Executing pending tasks in single mode...")
|
|
717
|
+
task_info = state.browser_tasks[0]
|
|
718
|
+
task_id = nanoid.generate(size=5)
|
|
719
|
+
bu_agents_workdir = state.vibesurf_agent.file_system.get_dir() / "bu_agents"
|
|
720
|
+
bu_agents_workdir.mkdir(parents=True, exist_ok=True)
|
|
721
|
+
task_description = task_info.get('task', '')
|
|
722
|
+
if not task_description:
|
|
723
|
+
return BrowserTaskResult(
|
|
724
|
+
agent_id=f"{task_id}-{1:03d}",
|
|
725
|
+
task='',
|
|
726
|
+
agent_workdir=f"bu_agents/{task_id}-{1:03d}",
|
|
727
|
+
success=False,
|
|
728
|
+
error="Task description is empty. Please provide a valid task description for browser use agent.",
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
task_files = task_info.get('task_files', [])
|
|
732
|
+
bu_agent_workdir = bu_agents_workdir / f"{task_id}-{1:03d}"
|
|
733
|
+
bu_agent_workdir.mkdir(parents=True, exist_ok=True)
|
|
734
|
+
agent_name = f"browser_use_agent-{task_id}-{1:03d}"
|
|
735
|
+
agent_id = f"bu_agent-{task_id}-{1:03d}"
|
|
736
|
+
# Log agent creation
|
|
737
|
+
await log_agent_activity(state, agent_name, "working", f"{task_description}")
|
|
875
738
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
await
|
|
739
|
+
try:
|
|
740
|
+
vibesurf_tools = state.vibesurf_agent.tools
|
|
741
|
+
bu_tools = BrowserUseTools()
|
|
742
|
+
for mcp_server_name, mcp_client in vibesurf_tools.mcp_clients.items():
|
|
743
|
+
await mcp_client.register_to_tools(
|
|
744
|
+
tools=bu_tools,
|
|
745
|
+
prefix=f"mcp.{mcp_server_name}."
|
|
746
|
+
)
|
|
747
|
+
available_file_paths = []
|
|
748
|
+
if task_files:
|
|
749
|
+
for task_file in task_files:
|
|
750
|
+
upload_workdir = bu_agent_workdir / "upload_files"
|
|
751
|
+
upload_workdir.mkdir(parents=True, exist_ok=True)
|
|
752
|
+
task_file_path = state.vibesurf_agent.file_system.get_absolute_path(task_file)
|
|
753
|
+
if os.path.exists(task_file_path):
|
|
754
|
+
logger.info(f"Copy {task_file_path} to {upload_workdir}")
|
|
755
|
+
shutil.copy(task_file_path, str(upload_workdir))
|
|
756
|
+
available_file_paths.append(os.path.join("upload_files", os.path.basename(task_file_path)))
|
|
757
|
+
|
|
758
|
+
# Create BrowserUseAgent for each task
|
|
759
|
+
if available_file_paths:
|
|
760
|
+
upload_files_md = '\n'.join(available_file_paths)
|
|
761
|
+
bu_task = task_description + f"\nNecessary files for this task:\n{upload_files_md}\n"
|
|
762
|
+
else:
|
|
763
|
+
bu_task = task_description
|
|
764
|
+
|
|
765
|
+
step_callback = create_browser_agent_step_callback(state, agent_name)
|
|
766
|
+
main_browser_session = state.vibesurf_agent.browser_manager.main_browser_session
|
|
767
|
+
if task_info.get("tab_id", None):
|
|
768
|
+
tab_id = task_info.get("tab_id")
|
|
769
|
+
target_id = await main_browser_session.get_target_id_from_tab_id(tab_id)
|
|
770
|
+
await main_browser_session.get_or_create_cdp_session(target_id=target_id)
|
|
881
771
|
else:
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
772
|
+
new_target = await main_browser_session.cdp_client.send.Target.createTarget(
|
|
773
|
+
params={'url': 'about:blank'})
|
|
774
|
+
target_id = new_target["targetId"]
|
|
775
|
+
await main_browser_session.get_or_create_cdp_session(target_id=target_id)
|
|
776
|
+
agent = BrowserUseAgent(
|
|
777
|
+
task=bu_task,
|
|
778
|
+
llm=state.vibesurf_agent.llm,
|
|
779
|
+
browser_session=main_browser_session,
|
|
780
|
+
tools=bu_tools,
|
|
781
|
+
task_id=f"{task_id}-{1:03d}",
|
|
782
|
+
file_system_path=str(bu_agent_workdir),
|
|
783
|
+
register_new_step_callback=step_callback,
|
|
784
|
+
extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
|
|
785
|
+
token_cost_service=state.vibesurf_agent.token_cost_service
|
|
786
|
+
)
|
|
787
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
788
|
+
state.vibesurf_agent._running_agents[agent_id] = agent
|
|
789
|
+
logger.debug(f"🔗 Registered single agent {agent_id} for control coordination")
|
|
790
|
+
|
|
791
|
+
history = await agent.run()
|
|
792
|
+
bu_agent_workdir = f"bu_agents/{task_id}-{1:03d}"
|
|
793
|
+
important_files = []
|
|
794
|
+
if history and history.history and len(history.history[-1].result) > 0:
|
|
795
|
+
last_result = history.history[-1].result[-1]
|
|
796
|
+
important_files = last_result.attachments
|
|
797
|
+
if important_files:
|
|
798
|
+
important_files = [os.path.join(bu_agent_workdir, file_name) for file_name in important_files]
|
|
799
|
+
|
|
800
|
+
result = BrowserTaskResult(
|
|
801
|
+
agent_id=agent_id,
|
|
802
|
+
agent_workdir=bu_agent_workdir,
|
|
803
|
+
task=bu_task,
|
|
804
|
+
important_files=important_files,
|
|
805
|
+
success=history.is_successful(),
|
|
806
|
+
result=history.final_result() if hasattr(history, 'final_result') else "Task completed",
|
|
807
|
+
error=str(history.errors()) if history.has_errors() and not history.is_successful() else ""
|
|
808
|
+
)
|
|
885
809
|
|
|
886
|
-
|
|
810
|
+
# Log result
|
|
811
|
+
if result.success:
|
|
812
|
+
await log_agent_activity(state, agent_name, "result",
|
|
813
|
+
f"Task completed successfully: \n{result.result}")
|
|
814
|
+
else:
|
|
815
|
+
await log_agent_activity(state, agent_name, "error",
|
|
816
|
+
f"Task failed: {result.error}")
|
|
817
|
+
return result
|
|
887
818
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
task_id=f"{state.task_id}-{i}",
|
|
906
|
-
file_system_path=os.path.join(state.task_dir, f"{state.task_id}-{i}"),
|
|
907
|
-
register_new_step_callback=step_callback,
|
|
908
|
-
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."
|
|
909
|
-
)
|
|
819
|
+
except Exception as e:
|
|
820
|
+
import traceback
|
|
821
|
+
traceback.print_exc()
|
|
822
|
+
|
|
823
|
+
bu_agent_workdir = f"bu_agents/{task_id}-{1:03d}"
|
|
824
|
+
return BrowserTaskResult(
|
|
825
|
+
agent_id=agent_id,
|
|
826
|
+
agent_workdir=bu_agent_workdir,
|
|
827
|
+
task=task_description,
|
|
828
|
+
success=False,
|
|
829
|
+
error=str(e)
|
|
830
|
+
)
|
|
831
|
+
finally:
|
|
832
|
+
# Remove agent from control tracking
|
|
833
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
834
|
+
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
835
|
+
logger.debug(f"🔗 Unregistered single agent {agent_id} from control coordination")
|
|
910
836
|
|
|
911
|
-
# Track agent in VibeSurfAgent for control coordination
|
|
912
|
-
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
913
|
-
state.vibesurf_agent._running_agents[agent_id] = agent
|
|
914
|
-
logger.debug(f"🔗 Registered single agent {agent_id} for control coordination")
|
|
915
837
|
|
|
916
|
-
|
|
917
|
-
|
|
838
|
+
async def report_task_execution_node(state: VibeSurfState) -> VibeSurfState:
|
|
839
|
+
"""
|
|
840
|
+
Execute HTML report generation task assigned by supervisor agent
|
|
841
|
+
"""
|
|
842
|
+
return await control_aware_node(_report_task_execution_node_impl, state, "report_task_execution")
|
|
918
843
|
|
|
919
|
-
result = BrowserTaskResult(
|
|
920
|
-
agent_id=agent_id,
|
|
921
|
-
task=task,
|
|
922
|
-
success=history.is_successful(),
|
|
923
|
-
result=history.final_result() if hasattr(history, 'final_result') else "Task completed",
|
|
924
|
-
error=str(history.errors()) if history.has_errors() and not history.is_successful() else ""
|
|
925
|
-
)
|
|
926
844
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
f"Task completed successfully: \n{result.result}")
|
|
931
|
-
else:
|
|
932
|
-
log_agent_activity(state, f"browser_use_agent-{state.task_id[-4:]}", "error",
|
|
933
|
-
f"Task failed: {result.error}")
|
|
845
|
+
async def _report_task_execution_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
846
|
+
"""Implementation of report task execution node"""
|
|
847
|
+
logger.info("📄 Executing HTML report generation task...")
|
|
934
848
|
|
|
935
|
-
|
|
936
|
-
finally:
|
|
937
|
-
# Remove agent from control tracking
|
|
938
|
-
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
939
|
-
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
940
|
-
logger.debug(f"🔗 Unregistered single agent {agent_id} from control coordination")
|
|
849
|
+
agent_name = "report_writer_agent"
|
|
941
850
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
851
|
+
# Log agent activity
|
|
852
|
+
await log_agent_activity(state, agent_name, "working", "Generating HTML report")
|
|
853
|
+
|
|
854
|
+
try:
|
|
855
|
+
# Create step callback for report writer agent
|
|
856
|
+
step_callback = create_report_writer_step_callback(state, agent_name)
|
|
857
|
+
|
|
858
|
+
# Use ReportWriterAgent to generate HTML report
|
|
859
|
+
report_writer = ReportWriterAgent(
|
|
860
|
+
llm=state.vibesurf_agent.llm,
|
|
861
|
+
workspace_dir=str(state.vibesurf_agent.file_system.get_dir()),
|
|
862
|
+
step_callback=step_callback,
|
|
863
|
+
thinking_mode=state.vibesurf_agent.thinking_mode,
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
# Register report writer agent for control coordination
|
|
867
|
+
agent_id = "report_writer_agent"
|
|
868
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
869
|
+
state.vibesurf_agent._running_agents[agent_id] = report_writer
|
|
870
|
+
logger.debug(f"🔗 Registered report writer agent for control coordination")
|
|
871
|
+
|
|
872
|
+
try:
|
|
873
|
+
action_params = state.action_params
|
|
874
|
+
report_task = action_params.get('task', [])
|
|
875
|
+
report_information = {
|
|
876
|
+
"browser_results": [bu_result.model_dump() for bu_result in state.browser_results if bu_result]
|
|
877
|
+
}
|
|
878
|
+
report_data = {
|
|
879
|
+
"report_task": report_task,
|
|
880
|
+
"report_information": report_information
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
report_result = await report_writer.generate_report(report_data)
|
|
884
|
+
state.generated_report_result = report_result
|
|
885
|
+
|
|
886
|
+
# Return to vibesurf agent for next decision
|
|
887
|
+
state.current_step = "vibesurf_agent"
|
|
888
|
+
|
|
889
|
+
if report_result.success:
|
|
890
|
+
await log_agent_activity(state, agent_name, "result",
|
|
891
|
+
f"✅ HTML report generated successfully: {report_result.msg}\nPath: `{report_result.report_path}`")
|
|
892
|
+
logger.info(f"✅ Report generated successfully: {report_result.report_path}")
|
|
893
|
+
else:
|
|
894
|
+
await log_agent_activity(state, agent_name, "error",
|
|
895
|
+
f"❌ Report generation failed: {report_result.msg}\nPath: `{report_result.report_path}`")
|
|
896
|
+
logger.warning(f"⚠️ Report generation failed: {report_result.msg}")
|
|
897
|
+
|
|
898
|
+
finally:
|
|
899
|
+
# Remove report writer agent from control tracking
|
|
900
|
+
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
901
|
+
state.vibesurf_agent._running_agents.pop(agent_id, None)
|
|
902
|
+
logger.debug(f"🔗 Unregistered report writer agent from control coordination")
|
|
903
|
+
|
|
904
|
+
return state
|
|
952
905
|
|
|
953
|
-
|
|
906
|
+
except Exception as e:
|
|
907
|
+
logger.error(f"❌ Report generation failed: {e}")
|
|
908
|
+
state.current_step = "vibesurf_agent"
|
|
909
|
+
await log_agent_activity(state, agent_name, "error", f"Report generation failed: {str(e)}")
|
|
910
|
+
return state
|
|
954
911
|
|
|
955
912
|
|
|
956
|
-
def
|
|
957
|
-
"""Route based on
|
|
913
|
+
def route_after_vibesurf_agent(state: VibeSurfState) -> str:
|
|
914
|
+
"""Route based on vibesurf agent decisions"""
|
|
958
915
|
if state.current_step == "browser_task_execution":
|
|
959
916
|
return "browser_task_execution"
|
|
960
917
|
elif state.current_step == "report_task_execution":
|
|
961
918
|
return "report_task_execution"
|
|
962
|
-
elif state.current_step == "
|
|
963
|
-
return "
|
|
964
|
-
elif state.
|
|
965
|
-
return "
|
|
966
|
-
elif state.current_step == "supervisor_agent":
|
|
967
|
-
return "supervisor_agent" # Continue in supervisor loop
|
|
919
|
+
elif state.current_step == "vibesurf_agent":
|
|
920
|
+
return "vibesurf_agent" # Continue in vibesurf agent loop
|
|
921
|
+
elif state.is_complete:
|
|
922
|
+
return "END" # task_done sets is_complete=True, go directly to END
|
|
968
923
|
else:
|
|
969
924
|
return "END" # Default fallback - complete workflow
|
|
970
925
|
|
|
971
926
|
|
|
972
927
|
def route_after_browser_task_execution(state: VibeSurfState) -> str:
|
|
973
|
-
"""Route back to
|
|
974
|
-
return "
|
|
928
|
+
"""Route back to vibesurf agent after browser task completion"""
|
|
929
|
+
return "vibesurf_agent"
|
|
975
930
|
|
|
976
931
|
|
|
977
932
|
def route_after_report_task_execution(state: VibeSurfState) -> str:
|
|
978
|
-
"""Route back to
|
|
979
|
-
return "
|
|
933
|
+
"""Route back to vibesurf agent after report task completion"""
|
|
934
|
+
return "vibesurf_agent"
|
|
980
935
|
|
|
981
936
|
|
|
982
937
|
def should_continue(state: VibeSurfState) -> str:
|
|
@@ -988,38 +943,36 @@ def should_continue(state: VibeSurfState) -> str:
|
|
|
988
943
|
|
|
989
944
|
|
|
990
945
|
def create_vibe_surf_workflow() -> StateGraph:
|
|
991
|
-
"""Create the simplified LangGraph workflow with supervisor agent as core
|
|
946
|
+
"""Create the simplified LangGraph workflow with supervisor agent as core tools"""
|
|
992
947
|
|
|
993
948
|
workflow = StateGraph(VibeSurfState)
|
|
994
949
|
|
|
995
950
|
# Add nodes for simplified architecture
|
|
996
|
-
workflow.add_node("
|
|
951
|
+
workflow.add_node("vibesurf_agent", vibesurf_agent_node)
|
|
997
952
|
workflow.add_node("browser_task_execution", browser_task_execution_node)
|
|
998
953
|
workflow.add_node("report_task_execution", report_task_execution_node)
|
|
999
954
|
|
|
1000
955
|
# Set entry point
|
|
1001
|
-
workflow.set_entry_point("
|
|
956
|
+
workflow.set_entry_point("vibesurf_agent")
|
|
1002
957
|
|
|
1003
|
-
#
|
|
958
|
+
# VibeSurf agent routes to different execution nodes or END
|
|
1004
959
|
workflow.add_conditional_edges(
|
|
1005
|
-
"
|
|
1006
|
-
|
|
960
|
+
"vibesurf_agent",
|
|
961
|
+
route_after_vibesurf_agent,
|
|
1007
962
|
{
|
|
1008
963
|
"browser_task_execution": "browser_task_execution",
|
|
1009
964
|
"report_task_execution": "report_task_execution",
|
|
1010
|
-
"
|
|
1011
|
-
"supervisor_agent": "supervisor_agent",
|
|
1012
|
-
"simple_response": END,
|
|
965
|
+
"vibesurf_agent": "vibesurf_agent",
|
|
1013
966
|
"END": END
|
|
1014
967
|
}
|
|
1015
968
|
)
|
|
1016
969
|
|
|
1017
|
-
# Execution nodes return to
|
|
970
|
+
# Execution nodes return to vibesurf agent
|
|
1018
971
|
workflow.add_conditional_edges(
|
|
1019
972
|
"browser_task_execution",
|
|
1020
973
|
route_after_browser_task_execution,
|
|
1021
974
|
{
|
|
1022
|
-
"
|
|
975
|
+
"vibesurf_agent": "vibesurf_agent"
|
|
1023
976
|
}
|
|
1024
977
|
)
|
|
1025
978
|
|
|
@@ -1027,7 +980,7 @@ def create_vibe_surf_workflow() -> StateGraph:
|
|
|
1027
980
|
"report_task_execution",
|
|
1028
981
|
route_after_report_task_execution,
|
|
1029
982
|
{
|
|
1030
|
-
"
|
|
983
|
+
"vibesurf_agent": "vibesurf_agent"
|
|
1031
984
|
}
|
|
1032
985
|
)
|
|
1033
986
|
|
|
@@ -1035,24 +988,32 @@ def create_vibe_surf_workflow() -> StateGraph:
|
|
|
1035
988
|
|
|
1036
989
|
|
|
1037
990
|
class VibeSurfAgent:
|
|
1038
|
-
"""Main LangGraph-based
|
|
991
|
+
"""Main LangGraph-based VibeSurf Agent"""
|
|
1039
992
|
|
|
1040
993
|
def __init__(
|
|
1041
994
|
self,
|
|
1042
995
|
llm: BaseChatModel,
|
|
1043
996
|
browser_manager: BrowserManager,
|
|
1044
|
-
|
|
997
|
+
tools: VibeSurfTools,
|
|
1045
998
|
workspace_dir: str = "./workspace",
|
|
999
|
+
thinking_mode: bool = True,
|
|
1000
|
+
calculate_token_cost: bool = True,
|
|
1046
1001
|
):
|
|
1047
1002
|
"""Initialize VibeSurfAgent with required components"""
|
|
1048
|
-
self.llm = llm
|
|
1049
|
-
self.
|
|
1050
|
-
self.
|
|
1003
|
+
self.llm: BaseChatModel = llm
|
|
1004
|
+
self.calculate_token_cost = calculate_token_cost
|
|
1005
|
+
self.token_cost_service = TokenCost(include_cost=calculate_token_cost)
|
|
1006
|
+
self.token_cost_service.register_llm(llm)
|
|
1007
|
+
self.browser_manager: BrowserManager = browser_manager
|
|
1008
|
+
self.tools: VibeSurfTools = tools
|
|
1051
1009
|
self.workspace_dir = workspace_dir
|
|
1052
1010
|
os.makedirs(self.workspace_dir, exist_ok=True)
|
|
1011
|
+
self.thinking_mode = thinking_mode
|
|
1012
|
+
|
|
1053
1013
|
self.cur_session_id = None
|
|
1054
|
-
self.
|
|
1055
|
-
self.
|
|
1014
|
+
self.file_system: Optional[CustomFileSystem] = None
|
|
1015
|
+
self.message_history = []
|
|
1016
|
+
self.activity_logs = []
|
|
1056
1017
|
|
|
1057
1018
|
# Create LangGraph workflow
|
|
1058
1019
|
self.workflow = create_vibe_surf_workflow()
|
|
@@ -1064,47 +1025,96 @@ class VibeSurfAgent:
|
|
|
1064
1025
|
self._running_agents: Dict[str, Any] = {} # Track running BrowserUseAgent instances
|
|
1065
1026
|
self._execution_task: Optional[asyncio.Task] = None
|
|
1066
1027
|
|
|
1067
|
-
logger.info("🌊
|
|
1068
|
-
|
|
1069
|
-
def load_message_history(self,
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
return
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1028
|
+
logger.info("🌊 VibeSurf Agent initialized with LangGraph workflow")
|
|
1029
|
+
|
|
1030
|
+
def load_message_history(self, session_id: Optional[str] = None) -> list:
|
|
1031
|
+
"""Load message history for a specific session, or return [] for new sessions"""
|
|
1032
|
+
if session_id is None:
|
|
1033
|
+
return []
|
|
1034
|
+
|
|
1035
|
+
session_message_history_path = os.path.join(self.workspace_dir, "sessions", session_id, "message_history.pkl")
|
|
1036
|
+
|
|
1037
|
+
if not os.path.exists(session_message_history_path):
|
|
1038
|
+
all_message_history_path = os.path.join(self.workspace_dir, "message_history.pkl")
|
|
1039
|
+
if os.path.exists(all_message_history_path):
|
|
1040
|
+
with open(all_message_history_path, "rb") as f:
|
|
1041
|
+
message_history_dict = pickle.load(f)
|
|
1042
|
+
if session_id in message_history_dict:
|
|
1043
|
+
return message_history_dict[session_id]
|
|
1044
|
+
logger.info(f"No message history found for session {session_id}, creating new")
|
|
1045
|
+
return []
|
|
1046
|
+
|
|
1047
|
+
try:
|
|
1048
|
+
with open(session_message_history_path, "rb") as f:
|
|
1049
|
+
message_history = pickle.load(f)
|
|
1050
|
+
logger.info(f"Loading message history for session {session_id} from {session_message_history_path}")
|
|
1051
|
+
return message_history
|
|
1052
|
+
except Exception as e:
|
|
1053
|
+
logger.error(f"Failed to load message history for session {session_id}: {e}")
|
|
1054
|
+
return []
|
|
1055
|
+
|
|
1056
|
+
def save_message_history(self, session_id: Optional[str] = None):
|
|
1057
|
+
"""Save message history for a specific session"""
|
|
1058
|
+
if session_id is None:
|
|
1059
|
+
return
|
|
1060
|
+
|
|
1061
|
+
# Create session directory if it doesn't exist
|
|
1062
|
+
session_dir = os.path.join(self.workspace_dir, "sessions", session_id)
|
|
1063
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
1064
|
+
|
|
1065
|
+
session_message_history_path = os.path.join(session_dir, "message_history.pkl")
|
|
1066
|
+
|
|
1067
|
+
try:
|
|
1068
|
+
with open(session_message_history_path, "wb") as f:
|
|
1069
|
+
logger.info(f"Saving message history for session {session_id} to {session_message_history_path}")
|
|
1070
|
+
pickle.dump(self.message_history, f)
|
|
1071
|
+
except Exception as e:
|
|
1072
|
+
logger.error(f"Failed to save message history for session {session_id}: {e}")
|
|
1073
|
+
|
|
1074
|
+
def load_activity_logs(self, session_id: Optional[str] = None) -> list:
|
|
1075
|
+
"""Load activity logs for a specific session, or return [] for new sessions"""
|
|
1076
|
+
if session_id is None:
|
|
1077
|
+
return []
|
|
1078
|
+
|
|
1079
|
+
session_activity_logs_path = os.path.join(self.workspace_dir, "sessions", session_id, "activity_logs.pkl")
|
|
1080
|
+
|
|
1081
|
+
if not os.path.exists(session_activity_logs_path):
|
|
1082
|
+
# Adaptive to the older version
|
|
1083
|
+
all_activity_logs_path = os.path.join(self.workspace_dir, "activity_logs.pkl")
|
|
1084
|
+
if os.path.exists(all_activity_logs_path):
|
|
1085
|
+
with open(all_activity_logs_path, "rb") as f:
|
|
1086
|
+
activity_logs_dict = pickle.load(f)
|
|
1087
|
+
if session_id in activity_logs_dict:
|
|
1088
|
+
return activity_logs_dict[session_id]
|
|
1089
|
+
logger.info(f"No activity logs found for session {session_id}, creating new")
|
|
1090
|
+
return []
|
|
1091
|
+
|
|
1092
|
+
try:
|
|
1093
|
+
with open(session_activity_logs_path, "rb") as f:
|
|
1094
|
+
activity_logs = pickle.load(f)
|
|
1095
|
+
logger.info(f"Loading activity logs for session {session_id} from {session_activity_logs_path}")
|
|
1096
|
+
return activity_logs
|
|
1097
|
+
except Exception as e:
|
|
1098
|
+
logger.error(f"Failed to load activity logs for session {session_id}: {e}")
|
|
1099
|
+
return []
|
|
1100
|
+
|
|
1101
|
+
def save_activity_logs(self, session_id: Optional[str] = None):
|
|
1102
|
+
"""Save activity logs for a specific session"""
|
|
1103
|
+
if session_id is None:
|
|
1104
|
+
return
|
|
1105
|
+
|
|
1106
|
+
# Create session directory if it doesn't exist
|
|
1107
|
+
session_dir = os.path.join(self.workspace_dir, "sessions", session_id)
|
|
1108
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
1109
|
+
|
|
1110
|
+
session_activity_logs_path = os.path.join(session_dir, "activity_logs.pkl")
|
|
1111
|
+
|
|
1112
|
+
try:
|
|
1113
|
+
with open(session_activity_logs_path, "wb") as f:
|
|
1114
|
+
logger.info(f"Saving activity logs for session {session_id} to {session_activity_logs_path}")
|
|
1115
|
+
pickle.dump(self.activity_logs, f)
|
|
1116
|
+
except Exception as e:
|
|
1117
|
+
logger.error(f"Failed to save activity logs for session {session_id}: {e}")
|
|
1108
1118
|
|
|
1109
1119
|
async def stop(self, reason: str = None) -> ControlResult:
|
|
1110
1120
|
"""
|
|
@@ -1121,21 +1131,16 @@ class VibeSurfAgent:
|
|
|
1121
1131
|
reason = reason or "Manual stop requested"
|
|
1122
1132
|
logger.info(f"🛑 Stopping agent execution: {reason}")
|
|
1123
1133
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
supervisor_message_history.append(UserMessage(
|
|
1127
|
-
content=f"🛑 Stopping agent execution: {reason}"))
|
|
1134
|
+
self.message_history.append(UserMessage(
|
|
1135
|
+
content=f"🛑 Stopping agent execution: {reason}"))
|
|
1128
1136
|
|
|
1129
1137
|
if self._current_state:
|
|
1130
1138
|
self._current_state.should_stop = True
|
|
1131
1139
|
self._current_state.stopped = True
|
|
1132
|
-
self._current_state.control_timestamps["stopped"] = datetime.now()
|
|
1133
|
-
self._current_state.control_reasons["stopped"] = reason
|
|
1134
|
-
self._current_state.last_control_action = "stop"
|
|
1135
1140
|
|
|
1136
1141
|
# Stop all running agents with timeout
|
|
1137
1142
|
try:
|
|
1138
|
-
await asyncio.wait_for(self._stop_all_agents(
|
|
1143
|
+
await asyncio.wait_for(self._stop_all_agents(), timeout=3.0)
|
|
1139
1144
|
except asyncio.TimeoutError:
|
|
1140
1145
|
logger.warning("⚠️ Agent stopping timed out, continuing with task cancellation")
|
|
1141
1146
|
|
|
@@ -1186,19 +1191,14 @@ class VibeSurfAgent:
|
|
|
1186
1191
|
reason = reason or "Manual pause requested"
|
|
1187
1192
|
logger.info(f"⏸️ Pausing agent execution: {reason}")
|
|
1188
1193
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
supervisor_message_history.append(UserMessage(
|
|
1192
|
-
content=f"⏸️ Pausing agent execution: {reason}"))
|
|
1194
|
+
self.message_history.append(UserMessage(
|
|
1195
|
+
content=f"⏸️ Pausing agent execution: {reason}"))
|
|
1193
1196
|
|
|
1194
1197
|
if self._current_state:
|
|
1195
1198
|
self._current_state.should_pause = True
|
|
1196
|
-
self._current_state.control_timestamps["pause_requested"] = datetime.now()
|
|
1197
|
-
self._current_state.control_reasons["paused"] = reason
|
|
1198
|
-
self._current_state.last_control_action = "pause"
|
|
1199
1199
|
|
|
1200
1200
|
# Pause all running agents
|
|
1201
|
-
await self._pause_all_agents(
|
|
1201
|
+
await self._pause_all_agents()
|
|
1202
1202
|
|
|
1203
1203
|
logger.info(f"✅ VibeSurf execution paused: {reason}")
|
|
1204
1204
|
return ControlResult(
|
|
@@ -1231,20 +1231,15 @@ class VibeSurfAgent:
|
|
|
1231
1231
|
reason = reason or "Manual resume requested"
|
|
1232
1232
|
logger.info(f"▶️ Resuming agent execution: {reason}")
|
|
1233
1233
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
supervisor_message_history.append(UserMessage(
|
|
1237
|
-
content=f"▶️ Resuming agent execution: {reason}"))
|
|
1234
|
+
self.message_history.append(UserMessage(
|
|
1235
|
+
content=f"▶️ Resuming agent execution: {reason}"))
|
|
1238
1236
|
|
|
1239
1237
|
if self._current_state:
|
|
1240
1238
|
self._current_state.paused = False
|
|
1241
1239
|
self._current_state.should_pause = False
|
|
1242
|
-
self._current_state.control_timestamps["resumed"] = datetime.now()
|
|
1243
|
-
self._current_state.control_reasons["resumed"] = reason
|
|
1244
|
-
self._current_state.last_control_action = "resume"
|
|
1245
1240
|
|
|
1246
1241
|
# Resume all paused agents
|
|
1247
|
-
await self._resume_all_agents(
|
|
1242
|
+
await self._resume_all_agents()
|
|
1248
1243
|
|
|
1249
1244
|
logger.info(f"✅ VibeSurf execution resumed: {reason}")
|
|
1250
1245
|
return ControlResult(
|
|
@@ -1278,15 +1273,6 @@ class VibeSurfAgent:
|
|
|
1278
1273
|
reason = reason or f"Manual pause requested for agent {agent_id}"
|
|
1279
1274
|
logger.info(f"⏸️ Pausing agent {agent_id}: {reason}")
|
|
1280
1275
|
|
|
1281
|
-
# Update state tracking
|
|
1282
|
-
if self._current_state:
|
|
1283
|
-
self._current_state.paused_agents.add(agent_id)
|
|
1284
|
-
if agent_id not in self._current_state.agent_control_states:
|
|
1285
|
-
self._current_state.agent_control_states[agent_id] = {}
|
|
1286
|
-
self._current_state.agent_control_states[agent_id]["paused"] = True
|
|
1287
|
-
self._current_state.agent_control_states[agent_id]["pause_reason"] = reason
|
|
1288
|
-
self._current_state.agent_control_states[agent_id]["pause_timestamp"] = datetime.now()
|
|
1289
|
-
|
|
1290
1276
|
# Pause the specific agent if it's running
|
|
1291
1277
|
agent = self._running_agents.get(agent_id)
|
|
1292
1278
|
if agent:
|
|
@@ -1327,15 +1313,6 @@ class VibeSurfAgent:
|
|
|
1327
1313
|
reason = reason or f"Manual resume requested for agent {agent_id}"
|
|
1328
1314
|
logger.info(f"▶️ Resuming agent {agent_id}: {reason}")
|
|
1329
1315
|
|
|
1330
|
-
# Update state tracking
|
|
1331
|
-
if self._current_state:
|
|
1332
|
-
self._current_state.paused_agents.discard(agent_id)
|
|
1333
|
-
if agent_id not in self._current_state.agent_control_states:
|
|
1334
|
-
self._current_state.agent_control_states[agent_id] = {}
|
|
1335
|
-
self._current_state.agent_control_states[agent_id]["paused"] = False
|
|
1336
|
-
self._current_state.agent_control_states[agent_id]["resume_reason"] = reason
|
|
1337
|
-
self._current_state.agent_control_states[agent_id]["resume_timestamp"] = datetime.now()
|
|
1338
|
-
|
|
1339
1316
|
# Resume the specific agent if it's running
|
|
1340
1317
|
agent = self._running_agents.get(agent_id)
|
|
1341
1318
|
if agent:
|
|
@@ -1376,7 +1353,7 @@ class VibeSurfAgent:
|
|
|
1376
1353
|
elif self._current_state.paused or self._current_state.should_pause:
|
|
1377
1354
|
overall_status = "paused"
|
|
1378
1355
|
elif self._current_state.is_complete:
|
|
1379
|
-
overall_status = "
|
|
1356
|
+
overall_status = "completed"
|
|
1380
1357
|
else:
|
|
1381
1358
|
overall_status = "running"
|
|
1382
1359
|
|
|
@@ -1390,13 +1367,11 @@ class VibeSurfAgent:
|
|
|
1390
1367
|
error_message = None
|
|
1391
1368
|
pause_reason = None
|
|
1392
1369
|
|
|
1393
|
-
#
|
|
1394
|
-
if self._current_state and
|
|
1395
|
-
status = "paused"
|
|
1396
|
-
agent_state = self._current_state.agent_control_states.get(agent_id, {})
|
|
1397
|
-
pause_reason = agent_state.get("pause_reason")
|
|
1398
|
-
elif self._current_state and self._current_state.stopped:
|
|
1370
|
+
# Simplified status checking since paused_agents removed
|
|
1371
|
+
if self._current_state and self._current_state.stopped:
|
|
1399
1372
|
status = "stopped"
|
|
1373
|
+
elif self._current_state and self._current_state.paused:
|
|
1374
|
+
status = "paused"
|
|
1400
1375
|
|
|
1401
1376
|
# Get current action if available
|
|
1402
1377
|
if agent and hasattr(agent, 'state'):
|
|
@@ -1421,11 +1396,9 @@ class VibeSurfAgent:
|
|
|
1421
1396
|
if self._current_state:
|
|
1422
1397
|
progress = {
|
|
1423
1398
|
"current_step": self._current_state.current_step,
|
|
1424
|
-
"completed_todos": len(self._current_state.completed_todos),
|
|
1425
|
-
"total_todos": len(self._current_state.todo_list),
|
|
1426
|
-
"current_task_index": self._current_state.current_task_index,
|
|
1427
1399
|
"is_complete": self._current_state.is_complete,
|
|
1428
|
-
"
|
|
1400
|
+
"browser_tasks_count": len(self._current_state.browser_tasks),
|
|
1401
|
+
"browser_results_count": len(self._current_state.browser_results)
|
|
1429
1402
|
}
|
|
1430
1403
|
|
|
1431
1404
|
return VibeSurfStatus(
|
|
@@ -1443,7 +1416,7 @@ class VibeSurfAgent:
|
|
|
1443
1416
|
progress={"error": str(e)}
|
|
1444
1417
|
)
|
|
1445
1418
|
|
|
1446
|
-
async def _stop_all_agents(self
|
|
1419
|
+
async def _stop_all_agents(self) -> None:
|
|
1447
1420
|
"""Stop all running agents"""
|
|
1448
1421
|
for agent_id, agent in self._running_agents.items():
|
|
1449
1422
|
try:
|
|
@@ -1454,36 +1427,120 @@ class VibeSurfAgent:
|
|
|
1454
1427
|
except Exception as e:
|
|
1455
1428
|
logger.warning(f"⚠️ Failed to stop agent {agent_id}: {e}")
|
|
1456
1429
|
|
|
1457
|
-
async def _pause_all_agents(self
|
|
1430
|
+
async def _pause_all_agents(self) -> None:
|
|
1458
1431
|
"""Pause all running agents"""
|
|
1459
1432
|
for agent_id, agent in self._running_agents.items():
|
|
1460
1433
|
try:
|
|
1461
1434
|
if hasattr(agent, 'pause'):
|
|
1462
1435
|
await agent.pause()
|
|
1463
1436
|
logger.info(f"⏸️ Paused agent {agent_id}")
|
|
1464
|
-
|
|
1465
|
-
self._current_state.paused_agents.add(agent_id)
|
|
1437
|
+
# Note: paused_agents removed in simplified state
|
|
1466
1438
|
except Exception as e:
|
|
1467
1439
|
logger.warning(f"⚠️ Failed to pause agent {agent_id}: {e}")
|
|
1468
1440
|
|
|
1469
|
-
async def _resume_all_agents(self
|
|
1441
|
+
async def _resume_all_agents(self) -> None:
|
|
1470
1442
|
"""Resume all paused agents"""
|
|
1471
1443
|
for agent_id, agent in self._running_agents.items():
|
|
1472
1444
|
try:
|
|
1473
1445
|
if hasattr(agent, 'resume'):
|
|
1474
1446
|
await agent.resume()
|
|
1475
1447
|
logger.info(f"▶️ Resumed agent {agent_id}")
|
|
1476
|
-
|
|
1477
|
-
self._current_state.paused_agents.discard(agent_id)
|
|
1448
|
+
# Note: paused_agents removed in simplified state
|
|
1478
1449
|
except Exception as e:
|
|
1479
1450
|
logger.warning(f"⚠️ Failed to resume agent {agent_id}: {e}")
|
|
1480
1451
|
|
|
1452
|
+
def _create_sub_agent_prompt(self, new_task: str, agent_id: str) -> str:
|
|
1453
|
+
"""
|
|
1454
|
+
Create a generic prompt for sub-agents when receiving new tasks.
|
|
1455
|
+
This prompt is designed to work with any type of sub-agent.
|
|
1456
|
+
"""
|
|
1457
|
+
return f"""🔄 **New Task/Guidance from User:**
|
|
1458
|
+
|
|
1459
|
+
{new_task}
|
|
1460
|
+
|
|
1461
|
+
**Note:** As a sub-agent, you should evaluate whether this new task is relevant to your current work. You may:
|
|
1462
|
+
- **Use it** if it provides helpful guidance, tips, or corrections for your current task
|
|
1463
|
+
- **Use it** if it's a follow-up task that enhances your current work
|
|
1464
|
+
- **Ignore it** if it's unrelated to your specific responsibilities or doesn't apply to your current task
|
|
1465
|
+
|
|
1466
|
+
Please continue with your assigned work, incorporating this guidance only if it's relevant and helpful to your specific role and current task."""
|
|
1467
|
+
|
|
1468
|
+
async def add_new_task(self, new_task: str) -> None:
|
|
1469
|
+
"""
|
|
1470
|
+
Add a new task or follow-up instruction during execution.
|
|
1471
|
+
This can be user feedback, guidance, or additional requirements.
|
|
1472
|
+
|
|
1473
|
+
Args:
|
|
1474
|
+
new_task: The new task, guidance, or instruction from the user
|
|
1475
|
+
"""
|
|
1476
|
+
activity_entry = {
|
|
1477
|
+
"agent_name": 'user',
|
|
1478
|
+
"agent_status": 'request', # working, result, error
|
|
1479
|
+
"agent_msg": f"{new_task}"
|
|
1480
|
+
}
|
|
1481
|
+
self.activity_logs.append(activity_entry)
|
|
1482
|
+
|
|
1483
|
+
# Create an English prompt for the main agent
|
|
1484
|
+
prompt = f"""🔄 **New Task/Follow-up from User:**
|
|
1485
|
+
|
|
1486
|
+
{new_task}
|
|
1487
|
+
|
|
1488
|
+
**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."""
|
|
1489
|
+
|
|
1490
|
+
# Add to VibeSurf agent's message history
|
|
1491
|
+
self.message_history.append(UserMessage(content=prompt))
|
|
1492
|
+
logger.info(f"🌊 VibeSurf agent received new task: {new_task}")
|
|
1493
|
+
|
|
1494
|
+
# Propagate to all running sub-agents with generic sub-agent prompt
|
|
1495
|
+
if self._running_agents:
|
|
1496
|
+
logger.info(f"📡 Propagating new task to {len(self._running_agents)} running agents")
|
|
1497
|
+
for agent_id, agent in self._running_agents.items():
|
|
1498
|
+
try:
|
|
1499
|
+
if hasattr(agent, 'add_new_task'):
|
|
1500
|
+
# Use the generic sub-agent prompt
|
|
1501
|
+
sub_agent_prompt = self._create_sub_agent_prompt(new_task, agent_id)
|
|
1502
|
+
agent.add_new_task(sub_agent_prompt)
|
|
1503
|
+
logger.debug(f"✅ Sent new task to agent {agent_id}")
|
|
1504
|
+
else:
|
|
1505
|
+
logger.debug(f"⚠️ Agent {agent_id} doesn't support add_new_task")
|
|
1506
|
+
except Exception as e:
|
|
1507
|
+
logger.warning(f"⚠️ Failed to send new task to agent {agent_id}: {e}")
|
|
1508
|
+
else:
|
|
1509
|
+
logger.debug("📭 No running agents to propagate new task to")
|
|
1510
|
+
|
|
1511
|
+
async def process_upload_files(self, upload_files: Optional[List[str]] = None):
|
|
1512
|
+
if not upload_files:
|
|
1513
|
+
return []
|
|
1514
|
+
new_upload_files = []
|
|
1515
|
+
for ufile_path in upload_files:
|
|
1516
|
+
# timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
1517
|
+
dst_filename = f"upload_files/{os.path.basename(ufile_path)}"
|
|
1518
|
+
new_upload_files.append(dst_filename)
|
|
1519
|
+
return new_upload_files
|
|
1520
|
+
|
|
1521
|
+
def format_upload_files(self, upload_files: Optional[List[str]] = None, use_abspath: bool = False) -> str:
|
|
1522
|
+
"""Format uploaded file for LLM prompt"""
|
|
1523
|
+
if upload_files is None:
|
|
1524
|
+
return ""
|
|
1525
|
+
if use_abspath:
|
|
1526
|
+
file_urls = []
|
|
1527
|
+
for i, file_path in enumerate(upload_files):
|
|
1528
|
+
abs_file_path = self.file_system.get_absolute_path(file_path)
|
|
1529
|
+
normalized_path = abs_file_path.replace(os.path.sep, '/')
|
|
1530
|
+
file_url = f"{i + 1}. [{os.path.basename(file_path)}](file:///{normalized_path})"
|
|
1531
|
+
file_urls.append(file_url)
|
|
1532
|
+
return "\n".join(file_urls)
|
|
1533
|
+
else:
|
|
1534
|
+
return "\n".join(
|
|
1535
|
+
[f"{i + 1}. {file_path}" for i, file_path in enumerate(upload_files)])
|
|
1536
|
+
|
|
1481
1537
|
async def run(
|
|
1482
1538
|
self,
|
|
1483
1539
|
task: str,
|
|
1484
1540
|
upload_files: Optional[List[str]] = None,
|
|
1485
1541
|
session_id: Optional[str] = None,
|
|
1486
|
-
|
|
1542
|
+
thinking_mode: bool = True
|
|
1543
|
+
) -> str | None:
|
|
1487
1544
|
"""
|
|
1488
1545
|
Main execution method that returns markdown summary with control capabilities
|
|
1489
1546
|
|
|
@@ -1494,53 +1551,52 @@ class VibeSurfAgent:
|
|
|
1494
1551
|
Returns:
|
|
1495
1552
|
str: Markdown summary of execution results
|
|
1496
1553
|
"""
|
|
1497
|
-
logger.info(f"🚀 Starting VibeSurfAgent execution for task: {task
|
|
1498
|
-
agent_activity_logs = None
|
|
1554
|
+
logger.info(f"🚀 Starting VibeSurfAgent execution for task: {task}. Powered by LLM model: {self.llm.model_name}")
|
|
1499
1555
|
try:
|
|
1556
|
+
self.thinking_mode = thinking_mode
|
|
1500
1557
|
session_id = session_id or self.cur_session_id or uuid7str()
|
|
1501
1558
|
if session_id != self.cur_session_id:
|
|
1559
|
+
# Load session-specific data when switching sessions
|
|
1502
1560
|
self.cur_session_id = session_id
|
|
1561
|
+
self.message_history = self.load_message_history(session_id)
|
|
1562
|
+
self.activity_logs = self.load_activity_logs(session_id)
|
|
1563
|
+
session_dir = os.path.join(self.workspace_dir, "sessions", self.cur_session_id)
|
|
1564
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
1565
|
+
self.file_system = CustomFileSystem(session_dir)
|
|
1566
|
+
self.token_cost_service.clear_history()
|
|
1503
1567
|
|
|
1504
|
-
if self.cur_session_id not in self.message_history:
|
|
1505
|
-
logger.info(f"{self.cur_session_id} not found in message_history, create a new one.")
|
|
1506
|
-
self.message_history[self.cur_session_id] = []
|
|
1507
|
-
supervisor_message_history = self.message_history[self.cur_session_id]
|
|
1508
|
-
if not supervisor_message_history:
|
|
1509
|
-
supervisor_message_history.append(SystemMessage(content=SUPERVISOR_AGENT_SYSTEM_PROMPT))
|
|
1510
1568
|
if upload_files and not isinstance(upload_files, list):
|
|
1511
1569
|
upload_files = [upload_files]
|
|
1512
|
-
|
|
1570
|
+
upload_files = await self.process_upload_files(upload_files)
|
|
1571
|
+
|
|
1572
|
+
if not self.message_history:
|
|
1573
|
+
self.message_history.append(SystemMessage(content=VIBESURF_SYSTEM_PROMPT))
|
|
1574
|
+
|
|
1575
|
+
# Format processed upload files for prompt
|
|
1513
1576
|
user_request = f"* User's New Request:\n{task}\n"
|
|
1514
1577
|
if upload_files:
|
|
1578
|
+
upload_files_md = self.format_upload_files(upload_files)
|
|
1515
1579
|
user_request += f"* User Uploaded Files:\n{upload_files_md}\n"
|
|
1516
|
-
|
|
1517
|
-
UserMessage(
|
|
1518
|
-
content=user_request)
|
|
1580
|
+
self.message_history.append(
|
|
1581
|
+
UserMessage(content=user_request)
|
|
1519
1582
|
)
|
|
1520
1583
|
logger.info(user_request)
|
|
1521
1584
|
|
|
1522
|
-
|
|
1523
|
-
self.activity_logs[self.cur_session_id] = []
|
|
1524
|
-
agent_activity_logs = self.activity_logs[self.cur_session_id]
|
|
1585
|
+
abs_upload_files_md = self.format_upload_files(upload_files, use_abspath=True)
|
|
1525
1586
|
activity_entry = {
|
|
1526
1587
|
"agent_name": 'user',
|
|
1527
1588
|
"agent_status": 'request', # working, result, error
|
|
1528
|
-
"agent_msg": f"{task}\nUpload Files:\n{
|
|
1589
|
+
"agent_msg": f"{task}\nUpload Files:\n{abs_upload_files_md}\n" if upload_files else f"{task}"
|
|
1529
1590
|
}
|
|
1530
|
-
|
|
1591
|
+
self.activity_logs.append(activity_entry)
|
|
1531
1592
|
|
|
1532
|
-
# Initialize state
|
|
1593
|
+
# Initialize state first (needed for file processing)
|
|
1533
1594
|
initial_state = VibeSurfState(
|
|
1534
1595
|
original_task=task,
|
|
1535
1596
|
upload_files=upload_files or [],
|
|
1536
|
-
workspace_dir=self.workspace_dir,
|
|
1537
|
-
browser_manager=self.browser_manager,
|
|
1538
|
-
vibesurf_controller=self.controller,
|
|
1539
|
-
agent_activity_logs=agent_activity_logs,
|
|
1540
|
-
supervisor_message_history=supervisor_message_history,
|
|
1541
|
-
llm=self.llm,
|
|
1542
1597
|
session_id=session_id,
|
|
1543
|
-
|
|
1598
|
+
current_workspace_dir=os.path.join(self.workspace_dir, "sessions", self.cur_session_id),
|
|
1599
|
+
vibesurf_agent=self,
|
|
1544
1600
|
)
|
|
1545
1601
|
|
|
1546
1602
|
# Set current state for control operations
|
|
@@ -1575,36 +1631,39 @@ class VibeSurfAgent:
|
|
|
1575
1631
|
except asyncio.CancelledError:
|
|
1576
1632
|
logger.info("🛑 VibeSurfAgent execution was cancelled")
|
|
1577
1633
|
# Add cancellation activity log
|
|
1578
|
-
if
|
|
1634
|
+
if self.activity_logs:
|
|
1579
1635
|
activity_entry = {
|
|
1580
1636
|
"agent_name": "VibeSurfAgent",
|
|
1581
1637
|
"agent_status": "cancelled",
|
|
1582
1638
|
"agent_msg": "Task execution was cancelled by user request."
|
|
1583
1639
|
}
|
|
1584
|
-
|
|
1640
|
+
self.activity_logs.append(activity_entry)
|
|
1585
1641
|
return f"# Task Execution Cancelled\n\n**Task:** {task}\n\nExecution was stopped by user request."
|
|
1586
1642
|
except Exception as e:
|
|
1643
|
+
import traceback
|
|
1644
|
+
traceback.print_exc()
|
|
1587
1645
|
logger.error(f"❌ VibeSurfAgent execution failed: {e}")
|
|
1588
1646
|
# Add error activity log
|
|
1589
|
-
if
|
|
1647
|
+
if self.activity_logs:
|
|
1590
1648
|
activity_entry = {
|
|
1591
1649
|
"agent_name": "VibeSurfAgent",
|
|
1592
1650
|
"agent_status": "error",
|
|
1593
1651
|
"agent_msg": f"Task execution failed: {str(e)}"
|
|
1594
1652
|
}
|
|
1595
|
-
|
|
1653
|
+
self.activity_logs.append(activity_entry)
|
|
1596
1654
|
return f"# Task Execution Failed\n\n**Task:** {task}\n\n**Error:** {str(e)}\n\nPlease try again or contact support."
|
|
1597
1655
|
finally:
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
#
|
|
1606
|
-
self.
|
|
1607
|
-
|
|
1656
|
+
|
|
1657
|
+
activity_entry = {
|
|
1658
|
+
"agent_name": "VibeSurfAgent",
|
|
1659
|
+
"agent_status": "done", # working, result, error
|
|
1660
|
+
"agent_msg": "Finish Task."
|
|
1661
|
+
}
|
|
1662
|
+
self.activity_logs.append(activity_entry)
|
|
1663
|
+
# Save session-specific data
|
|
1664
|
+
if self.cur_session_id:
|
|
1665
|
+
self.save_message_history(self.cur_session_id)
|
|
1666
|
+
self.save_activity_logs(self.cur_session_id)
|
|
1608
1667
|
async with self._control_lock:
|
|
1609
1668
|
self._current_state = None
|
|
1610
1669
|
self._execution_task = None
|
|
@@ -1614,44 +1673,40 @@ class VibeSurfAgent:
|
|
|
1614
1673
|
List[Dict]]:
|
|
1615
1674
|
if session_id is None:
|
|
1616
1675
|
session_id = self.cur_session_id
|
|
1617
|
-
|
|
1618
|
-
logger.debug(f"📊 GET_ACTIVITY_LOGS DEBUG - Session: {session_id}, Message Index: {message_index}, Current Session: {self.cur_session_id}")
|
|
1619
|
-
|
|
1676
|
+
|
|
1620
1677
|
# Ensure session_id exists in activity_logs
|
|
1621
|
-
if session_id
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1678
|
+
if session_id != self.cur_session_id:
|
|
1679
|
+
session_logs = self.load_activity_logs(session_id)
|
|
1680
|
+
else:
|
|
1681
|
+
session_logs = self.activity_logs
|
|
1682
|
+
|
|
1626
1683
|
logger.debug(f"📋 Session {session_id} has {len(session_logs)} activity logs")
|
|
1627
|
-
|
|
1684
|
+
|
|
1628
1685
|
if message_index is None:
|
|
1629
1686
|
logger.debug(f"📤 Returning all {len(session_logs)} activity logs for session {session_id}")
|
|
1630
1687
|
return session_logs
|
|
1631
1688
|
else:
|
|
1632
1689
|
if message_index >= len(session_logs):
|
|
1633
|
-
logger.debug(
|
|
1690
|
+
logger.debug(
|
|
1691
|
+
f"⚠️ Message index {message_index} out of range for session {session_id} (max index: {len(session_logs) - 1})")
|
|
1634
1692
|
return None
|
|
1635
1693
|
else:
|
|
1636
1694
|
activity_log = session_logs[message_index]
|
|
1637
|
-
logger.debug(
|
|
1695
|
+
logger.debug(
|
|
1696
|
+
f"📤 Returning activity log at index {message_index}: {activity_log.get('agent_name', 'unknown')} - {activity_log.get('agent_status', 'unknown')}")
|
|
1638
1697
|
return activity_log
|
|
1639
1698
|
|
|
1640
1699
|
async def _get_result(self, state) -> str:
|
|
1641
1700
|
"""Get the final result from execution with simplified workflow support"""
|
|
1642
1701
|
# Handle both dict and dataclass state types due to LangGraph serialization
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
final_summary = state.get('final_summary') if isinstance(state, dict) else getattr(state, 'final_summary', None)
|
|
1702
|
+
final_response = state.get('final_response') if isinstance(state, dict) else getattr(state, 'final_response',
|
|
1703
|
+
None)
|
|
1646
1704
|
original_task = state.get('original_task') if isinstance(state, dict) else getattr(state, 'original_task',
|
|
1647
1705
|
'Unknown task')
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
if simple_response:
|
|
1651
|
-
return f"# Response\n\n{simple_response}"
|
|
1652
|
-
elif final_summary:
|
|
1653
|
-
return final_summary
|
|
1706
|
+
if final_response:
|
|
1707
|
+
return final_response
|
|
1654
1708
|
else:
|
|
1655
1709
|
return f"# Task Execution Completed\n\n**Task:** {original_task}\n\nTask execution completed but no detailed result available."
|
|
1656
1710
|
|
|
1711
|
+
|
|
1657
1712
|
workflow = create_vibe_surf_workflow()
|