vibesurf 0.1.16__tar.gz → 0.1.18__tar.gz

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

Potentially problematic release.


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

Files changed (115) hide show
  1. {vibesurf-0.1.16 → vibesurf-0.1.18}/PKG-INFO +1 -1
  2. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/_version.py +3 -3
  3. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/browser_use_agent.py +6 -12
  4. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/report_writer_agent.py +3 -3
  5. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/vibe_surf_agent.py +17 -14
  6. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/views.py +4 -2
  7. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/models.py +3 -0
  8. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/task.py +3 -1
  9. vibesurf-0.1.18/vibe_surf/backend/database/manager.py +318 -0
  10. vibesurf-0.1.18/vibe_surf/backend/database/migrations/v001_initial_schema.sql +118 -0
  11. vibesurf-0.1.18/vibe_surf/backend/database/migrations/v002_add_agent_mode.sql +6 -0
  12. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/models.py +4 -1
  13. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/queries.py +4 -2
  14. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/shared_state.py +5 -3
  15. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/api-client.js +4 -2
  16. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/ui-manager.js +27 -1
  17. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/sidepanel.html +5 -0
  18. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/input.css +11 -3
  19. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/llm/openai_compatible.py +1 -2
  20. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/PKG-INFO +1 -1
  21. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/SOURCES.txt +4 -6
  22. vibesurf-0.1.16/vibe_surf/backend/database/manager.py +0 -135
  23. vibesurf-0.1.16/vibe_surf/backend/migrations/__init__.py +0 -16
  24. vibesurf-0.1.16/vibe_surf/backend/migrations/init_db.py +0 -303
  25. vibesurf-0.1.16/vibe_surf/backend/migrations/seed_data.py +0 -236
  26. {vibesurf-0.1.16 → vibesurf-0.1.18}/.env.example +0 -0
  27. {vibesurf-0.1.16 → vibesurf-0.1.18}/.github/workflows/publish.yml +0 -0
  28. {vibesurf-0.1.16 → vibesurf-0.1.18}/.gitignore +0 -0
  29. {vibesurf-0.1.16 → vibesurf-0.1.18}/.python-version +0 -0
  30. {vibesurf-0.1.16 → vibesurf-0.1.18}/LICENSE +0 -0
  31. {vibesurf-0.1.16 → vibesurf-0.1.18}/MANIFEST.in +0 -0
  32. {vibesurf-0.1.16 → vibesurf-0.1.18}/README.md +0 -0
  33. {vibesurf-0.1.16 → vibesurf-0.1.18}/docs/EXECUTABLE_BUILD.md +0 -0
  34. {vibesurf-0.1.16 → vibesurf-0.1.18}/docs/PYPI_SETUP.md +0 -0
  35. {vibesurf-0.1.16 → vibesurf-0.1.18}/pyproject.toml +0 -0
  36. {vibesurf-0.1.16 → vibesurf-0.1.18}/scripts/build-local.bat +0 -0
  37. {vibesurf-0.1.16 → vibesurf-0.1.18}/scripts/build-local.sh +0 -0
  38. {vibesurf-0.1.16 → vibesurf-0.1.18}/setup.cfg +0 -0
  39. {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_agents.py +0 -0
  40. {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_backend_api.py +0 -0
  41. {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_browser.py +0 -0
  42. {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_tools.py +0 -0
  43. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/__init__.py +0 -0
  44. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/__init__.py +0 -0
  45. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/prompts/__init__.py +0 -0
  46. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/prompts/report_writer_prompt.py +0 -0
  47. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/prompts/vibe_surf_prompt.py +0 -0
  48. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/__init__.py +0 -0
  49. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/__init__.py +0 -0
  50. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/activity.py +0 -0
  51. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/browser.py +0 -0
  52. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/config.py +0 -0
  53. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/files.py +0 -0
  54. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/__init__.py +0 -0
  55. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/schemas.py +0 -0
  56. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/llm_config.py +0 -0
  57. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/main.py +0 -0
  58. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/utils/__init__.py +0 -0
  59. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/utils/encryption.py +0 -0
  60. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/utils/llm_factory.py +0 -0
  61. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/__init__.py +0 -0
  62. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/agen_browser_profile.py +0 -0
  63. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/agent_browser_session.py +0 -0
  64. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/browser_manager.py +0 -0
  65. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/utils.py +0 -0
  66. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/watchdogs/__init__.py +0 -0
  67. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/watchdogs/action_watchdog.py +0 -0
  68. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/watchdogs/dom_watchdog.py +0 -0
  69. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/background.js +0 -0
  70. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/config.js +0 -0
  71. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/content.js +0 -0
  72. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/dev-reload.js +0 -0
  73. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/convert-svg.js +0 -0
  74. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/logo-preview.html +0 -0
  75. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/logo.icns +0 -0
  76. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/logo.png +0 -0
  77. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/manifest.json +0 -0
  78. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/popup.html +0 -0
  79. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/file-manager.js +0 -0
  80. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/history-manager.js +0 -0
  81. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/main.js +0 -0
  82. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/markdown-it.min.js +0 -0
  83. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/modal-manager.js +0 -0
  84. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/session-manager.js +0 -0
  85. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/settings-manager.js +0 -0
  86. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/activity.css +0 -0
  87. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/animations.css +0 -0
  88. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/base.css +0 -0
  89. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/components.css +0 -0
  90. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/history-modal.css +0 -0
  91. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/layout.css +0 -0
  92. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/responsive.css +0 -0
  93. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-environment.css +0 -0
  94. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-forms.css +0 -0
  95. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-modal.css +0 -0
  96. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-profiles.css +0 -0
  97. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-responsive.css +0 -0
  98. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-utilities.css +0 -0
  99. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/variables.css +0 -0
  100. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/cli.py +0 -0
  101. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/common.py +0 -0
  102. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/llm/__init__.py +0 -0
  103. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/logger.py +0 -0
  104. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/__init__.py +0 -0
  105. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/browser_use_tools.py +0 -0
  106. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/file_system.py +0 -0
  107. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/mcp_client.py +0 -0
  108. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/report_writer_tools.py +0 -0
  109. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/vibesurf_tools.py +0 -0
  110. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/views.py +0 -0
  111. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/dependency_links.txt +0 -0
  112. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/entry_points.txt +0 -0
  113. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/requires.txt +0 -0
  114. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/top_level.txt +0 -0
  115. {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.spec +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibesurf
3
- Version: 0.1.16
3
+ Version: 0.1.18
4
4
  Summary: VibeSurf: A powerful browser assistant for vibe surfing
5
5
  Author: Shao Warm
6
6
  License: Apache-2.0
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.16'
32
- __version_tuple__ = version_tuple = (0, 1, 16)
31
+ __version__ = version = '0.1.18'
32
+ __version_tuple__ = version_tuple = (0, 1, 18)
33
33
 
34
- __commit_id__ = commit_id = 'gb9e253fde'
34
+ __commit_id__ = commit_id = 'g51b12c120'
@@ -566,12 +566,6 @@ class BrowserUseAgent(Agent):
566
566
  agent_run_error = 'Agent stopped programmatically'
567
567
  break
568
568
 
569
- while self.state.paused:
570
- await asyncio.sleep(0.2) # Small delay to prevent CPU spinning
571
- if self.state.stopped: # Allow stopping while paused
572
- agent_run_error = 'Agent stopped programmatically while paused'
573
- break
574
-
575
569
  if on_step_start is not None:
576
570
  await on_step_start(self)
577
571
 
@@ -820,12 +814,12 @@ class BrowserUseAgent(Agent):
820
814
  i = global_action_index + local_index
821
815
 
822
816
  # Original sequential execution logic continues here...
823
- if i > 0:
824
- # ONLY ALLOW TO CALL `done` IF IT IS A SINGLE ACTION
825
- if action.model_dump(exclude_unset=True).get('done') is not None:
826
- msg = f'Done action is allowed only as a single action - stopped after action {i} / {total_actions}.'
827
- self.logger.debug(msg)
828
- break
817
+ # if i > 0:
818
+ # # ONLY ALLOW TO CALL `done` IF IT IS A SINGLE ACTION
819
+ # if action.model_dump(exclude_unset=True).get('done') is not None:
820
+ # msg = f'Done action is allowed only as a single action - stopped after action {i} / {total_actions}.'
821
+ # self.logger.debug(msg)
822
+ # break
829
823
 
830
824
  # DOM synchronization check - verify element indexes are still valid AFTER first action
831
825
  if action.get_index() is not None and i != 0:
@@ -32,7 +32,7 @@ class ReportTaskResult(BaseModel):
32
32
  class ReportWriterAgent:
33
33
  """Agent responsible for generating HTML reports using LLM-controlled flow"""
34
34
 
35
- def __init__(self, llm: BaseChatModel, workspace_dir: str, step_callback=None, thinking_mode: bool = True):
35
+ def __init__(self, llm: BaseChatModel, workspace_dir: str, step_callback=None, use_thinking: bool = True):
36
36
  """
37
37
  Initialize ReportWriterAgent
38
38
 
@@ -44,7 +44,7 @@ class ReportWriterAgent:
44
44
  self.llm = llm
45
45
  self.workspace_dir = os.path.abspath(workspace_dir)
46
46
  self.step_callback = step_callback
47
- self.thinking_mode = thinking_mode
47
+ self.use_thinking = use_thinking
48
48
 
49
49
  # Initialize file system and tools
50
50
  self.file_system = CustomFileSystem(self.workspace_dir)
@@ -52,7 +52,7 @@ class ReportWriterAgent:
52
52
 
53
53
  # Setup action model and agent output
54
54
  self.ActionModel = self.tools.registry.create_action_model()
55
- if self.thinking_mode:
55
+ if self.use_thinking:
56
56
  self.AgentOutput = CustomAgentOutput.type_with_custom_actions(self.ActionModel)
57
57
  else:
58
58
  self.AgentOutput = CustomAgentOutput.type_with_custom_actions_no_thinking(self.ActionModel)
@@ -38,6 +38,7 @@ from vibe_surf.browser.browser_manager import BrowserManager
38
38
  from vibe_surf.tools.browser_use_tools import BrowserUseTools
39
39
  from vibe_surf.tools.vibesurf_tools import VibeSurfTools
40
40
  from vibe_surf.tools.file_system import CustomFileSystem
41
+ from vibe_surf.agents.views import VibeSurfAgentSettings
41
42
 
42
43
  from vibe_surf.logger import get_logger
43
44
 
@@ -213,11 +214,11 @@ def create_browser_agent_step_callback(state: VibeSurfState, agent_name: str):
213
214
  step_msg = f"## Step {step_num}\n\n"
214
215
 
215
216
  # Add thinking if present
216
- if agent_output.thinking:
217
+ if hasattr(agent_output, 'thinking') and agent_output.thinking:
217
218
  step_msg += f"**💡 Thinking:**\n{agent_output.thinking}\n\n"
218
219
 
219
220
  # Add evaluation if present
220
- if agent_output.evaluation_previous_goal:
221
+ if hasattr(agent_output, 'evaluation_previous_goal') and agent_output.evaluation_previous_goal:
221
222
  step_msg += f"**👍 Evaluation:**\n{agent_output.evaluation_previous_goal}\n\n"
222
223
 
223
224
  # Add memory if present
@@ -225,7 +226,7 @@ def create_browser_agent_step_callback(state: VibeSurfState, agent_name: str):
225
226
  # step_msg += f"**🧠 Memory:** {agent_output.memory}\n\n"
226
227
 
227
228
  # Add next goal if present
228
- if agent_output.next_goal:
229
+ if hasattr(agent_output, 'next_goal') and agent_output.next_goal:
229
230
  step_msg += f"**🎯 Next Goal:**\n{agent_output.next_goal}\n\n"
230
231
 
231
232
  # Add action summary
@@ -352,7 +353,7 @@ async def _vibesurf_agent_node_impl(state: VibeSurfState) -> VibeSurfState:
352
353
  # Create action model and agent output using VibeSurfTools
353
354
  vibesurf_agent = state.vibesurf_agent
354
355
  ActionModel = vibesurf_agent.tools.registry.create_action_model()
355
- if vibesurf_agent.thinking_mode:
356
+ if vibesurf_agent.settings.agent_mode == "thinking":
356
357
  AgentOutput = CustomAgentOutput.type_with_custom_actions(ActionModel)
357
358
  else:
358
359
  AgentOutput = CustomAgentOutput.type_with_custom_actions_no_thinking(ActionModel)
@@ -632,7 +633,9 @@ async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTa
632
633
  file_system_path=str(bu_agent_workdir),
633
634
  register_new_step_callback=step_callback,
634
635
  extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
635
- token_cost_service=state.vibesurf_agent.token_cost_service
636
+ token_cost_service=state.vibesurf_agent.token_cost_service,
637
+ flash_mode=state.vibesurf_agent.settings.agent_mode == "flash",
638
+ use_thinking=state.vibesurf_agent.settings.agent_mode == "thinking"
636
639
  )
637
640
  agents.append(agent)
638
641
 
@@ -783,7 +786,9 @@ async def execute_single_browser_tasks(state: VibeSurfState) -> BrowserTaskResul
783
786
  file_system_path=str(bu_agent_workdir),
784
787
  register_new_step_callback=step_callback,
785
788
  extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
786
- token_cost_service=state.vibesurf_agent.token_cost_service
789
+ token_cost_service=state.vibesurf_agent.token_cost_service,
790
+ flash_mode=state.vibesurf_agent.settings.agent_mode == "flash",
791
+ use_thinking=state.vibesurf_agent.settings.agent_mode == "thinking"
787
792
  )
788
793
  if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
789
794
  state.vibesurf_agent._running_agents[agent_id] = agent
@@ -861,7 +866,7 @@ async def _report_task_execution_node_impl(state: VibeSurfState) -> VibeSurfStat
861
866
  llm=state.vibesurf_agent.llm,
862
867
  workspace_dir=str(state.vibesurf_agent.file_system.get_dir()),
863
868
  step_callback=step_callback,
864
- thinking_mode=state.vibesurf_agent.thinking_mode,
869
+ use_thinking=state.vibesurf_agent.settings.agent_mode == "thinking",
865
870
  )
866
871
 
867
872
  # Register report writer agent for control coordination
@@ -997,19 +1002,17 @@ class VibeSurfAgent:
997
1002
  browser_manager: BrowserManager,
998
1003
  tools: VibeSurfTools,
999
1004
  workspace_dir: str = "./workspace",
1000
- thinking_mode: bool = True,
1001
- calculate_token_cost: bool = True,
1005
+ settings: Optional[VibeSurfAgentSettings] = None
1002
1006
  ):
1003
1007
  """Initialize VibeSurfAgent with required components"""
1004
1008
  self.llm: BaseChatModel = llm
1005
- self.calculate_token_cost = calculate_token_cost
1006
- self.token_cost_service = TokenCost(include_cost=calculate_token_cost)
1009
+ self.settings = settings or VibeSurfAgentSettings()
1010
+ self.token_cost_service = TokenCost(include_cost=self.settings.calculate_cost)
1007
1011
  self.token_cost_service.register_llm(llm)
1008
1012
  self.browser_manager: BrowserManager = browser_manager
1009
1013
  self.tools: VibeSurfTools = tools
1010
1014
  self.workspace_dir = workspace_dir
1011
1015
  os.makedirs(self.workspace_dir, exist_ok=True)
1012
- self.thinking_mode = thinking_mode
1013
1016
 
1014
1017
  self.cur_session_id = None
1015
1018
  self.file_system: Optional[CustomFileSystem] = None
@@ -1540,7 +1543,7 @@ Please continue with your assigned work, incorporating this guidance only if it'
1540
1543
  task: str,
1541
1544
  upload_files: Optional[List[str]] = None,
1542
1545
  session_id: Optional[str] = None,
1543
- thinking_mode: bool = True
1546
+ agent_mode: str = "thinking"
1544
1547
  ) -> str | None:
1545
1548
  """
1546
1549
  Main execution method that returns markdown summary with control capabilities
@@ -1554,7 +1557,7 @@ Please continue with your assigned work, incorporating this guidance only if it'
1554
1557
  """
1555
1558
  logger.info(f"🚀 Starting VibeSurfAgent execution for task: {task}. Powered by LLM model: {self.llm.model_name}")
1556
1559
  try:
1557
- self.thinking_mode = thinking_mode
1560
+ self.settings.agent_mode = agent_mode
1558
1561
  session_id = session_id or self.cur_session_id or uuid7str()
1559
1562
  if session_id != self.cur_session_id:
1560
1563
  # Load session-specific data when switching sessions
@@ -66,11 +66,13 @@ class VibeSurfAgentSettings(BaseModel):
66
66
  max_history_items: int | None = None
67
67
  include_token_cost: bool = False
68
68
 
69
- calculate_cost: bool = False
69
+ calculate_cost: bool = True
70
70
  include_tool_call_examples: bool = False
71
71
  llm_timeout: int = 60 # Timeout in seconds for LLM calls
72
72
  step_timeout: int = 180 # Timeout in seconds for each step
73
73
 
74
+ agent_mode: str = "thinking" # thinking, no-thinking, flash
75
+
74
76
 
75
77
  class CustomAgentOutput(BaseModel):
76
78
  model_config = ConfigDict(arbitrary_types_allowed=True, extra='forbid')
@@ -117,7 +119,7 @@ class CustomAgentOutput(BaseModel):
117
119
  return schema
118
120
 
119
121
  model = create_model(
120
- 'AgentOutput',
122
+ 'AgentOutputNoThinking',
121
123
  __base__=AgentOutputNoThinking,
122
124
  action=(
123
125
  list[custom_actions], # type: ignore
@@ -106,6 +106,7 @@ class TaskCreateRequest(BaseModel):
106
106
  llm_profile_name: str = Field(description="LLM profile name to use")
107
107
  upload_files_path: Optional[str] = Field(default=None, description="Path to uploaded files")
108
108
  mcp_server_config: Optional[Dict[str, Any]] = Field(default=None, description="MCP server configuration")
109
+ agent_mode: str = Field(default="thinking", description="Agent mode: 'thinking', 'no-thinking', or 'flash'")
109
110
 
110
111
  class TaskControlRequest(BaseModel):
111
112
  """Request model for task control operations (pause/resume/stop)"""
@@ -121,6 +122,7 @@ class TaskResponse(BaseModel):
121
122
  upload_files_path: Optional[str] = None
122
123
  workspace_dir: Optional[str] = None
123
124
  mcp_server_config: Optional[Dict[str, Any]] = None
125
+ agent_mode: str = "thinking"
124
126
  task_result: Optional[str] = None
125
127
  error_message: Optional[str] = None
126
128
  report_path: Optional[str] = None
@@ -145,6 +147,7 @@ class TaskResponse(BaseModel):
145
147
  upload_files_path=task.upload_files_path,
146
148
  workspace_dir=task.workspace_dir,
147
149
  mcp_server_config=task.mcp_server_config,
150
+ agent_mode=task.agent_mode,
148
151
  task_result=task.task_result,
149
152
  error_message=task.error_message,
150
153
  report_path=task.report_path,
@@ -117,7 +117,8 @@ async def submit_task(
117
117
  mcp_server_config=mcp_server_config,
118
118
  llm_profile_name=task_request.llm_profile_name,
119
119
  workspace_dir=workspace_dir,
120
- task_status="pending"
120
+ task_status="pending",
121
+ agent_mode=task_request.agent_mode
121
122
  )
122
123
  await db.commit()
123
124
 
@@ -129,6 +130,7 @@ async def submit_task(
129
130
  task=task_request.task_description,
130
131
  llm_profile_name=task_request.llm_profile_name,
131
132
  upload_files=task_request.upload_files_path,
133
+ agent_mode=task_request.agent_mode,
132
134
  db_session=db
133
135
  )
134
136
 
@@ -0,0 +1,318 @@
1
+ """
2
+ Database Manager for VibeSurf Session Management
3
+
4
+ Handles database connections, session management, and initialization
5
+ with optimized configuration for real-time operations.
6
+ """
7
+
8
+ import asyncio
9
+ import os
10
+ import glob
11
+ import pdb
12
+ import re
13
+ from pathlib import Path
14
+ from typing import AsyncGenerator, List, Tuple, Optional
15
+ import logging
16
+ import aiosqlite
17
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
18
+ from sqlalchemy.orm import sessionmaker
19
+ from sqlalchemy.pool import StaticPool
20
+ from .models import Base
21
+
22
+ from vibe_surf.logger import get_logger
23
+
24
+ logger = get_logger(__name__)
25
+
26
+
27
+ class DBMigrationManager:
28
+ """Simplified database migration manager."""
29
+
30
+ def __init__(self, db_path: str = None):
31
+ """Initialize migration manager
32
+
33
+ Args:
34
+ db_path: Database file path. Will be extracted from database_url if not provided.
35
+ """
36
+ if db_path is None:
37
+ # Extract database path from shared_state
38
+ from .. import shared_state
39
+ database_url = os.getenv(
40
+ 'VIBESURF_DATABASE_URL',
41
+ f'sqlite+aiosqlite:///{os.path.join(shared_state.workspace_dir, "vibe_surf.db")}'
42
+ )
43
+ self.db_path = database_url
44
+ else:
45
+ self.db_path = db_path
46
+
47
+ # Extract path from sqlite URL
48
+ if self.db_path.startswith('sqlite+aiosqlite:///'):
49
+ self.db_path = self.db_path[20:] # Remove 'sqlite+aiosqlite:///' prefix
50
+ else:
51
+ raise ValueError(f"Migration manager only supports SQLite databases, got: {self.db_path}")
52
+
53
+ self.migrations_dir = Path(__file__).parent / "migrations"
54
+
55
+ async def get_db_version(self) -> int:
56
+ """Get current database version."""
57
+ try:
58
+ async with aiosqlite.connect(self.db_path) as db:
59
+ cursor = await db.execute("PRAGMA user_version;")
60
+ result = await cursor.fetchone()
61
+ return result[0] if result else 0
62
+ except Exception:
63
+ return 0
64
+
65
+ async def set_db_version(self, version: int) -> None:
66
+ """Set database version."""
67
+ async with aiosqlite.connect(self.db_path) as db:
68
+ await db.execute(f"PRAGMA user_version = {version};")
69
+ await db.commit()
70
+
71
+ def get_migration_files(self) -> List[Tuple[int, str]]:
72
+ """Get migration files sorted by version. Returns (version, filepath) tuples."""
73
+ migrations = []
74
+ pattern = str(self.migrations_dir / "v*.sql")
75
+
76
+ for filepath in glob.glob(pattern):
77
+ filename = os.path.basename(filepath)
78
+ match = re.match(r'v(\d+)_.*\.sql$', filename)
79
+ if match:
80
+ version = int(match.group(1))
81
+ migrations.append((version, filepath))
82
+ else:
83
+ logger.warning(f"Migration file {filename} doesn't match pattern v000_description.sql")
84
+
85
+ return sorted(migrations, key=lambda x: x[0])
86
+
87
+ async def init_database(self) -> None:
88
+ """Initialize database directory if needed."""
89
+ db_dir = os.path.dirname(self.db_path)
90
+ os.makedirs(db_dir, exist_ok=True)
91
+
92
+ current_version = await self.get_db_version()
93
+
94
+ if current_version == 0:
95
+ logger.info("Database version is 0, will be initialized via migrations...")
96
+ else:
97
+ logger.info(f"Database already exists (version {current_version})")
98
+
99
+ async def apply_migrations(self, target_version: Optional[int] = None) -> int:
100
+ """Apply pending migrations. Returns final version."""
101
+ await self.init_database() # Ensure database exists
102
+
103
+ current_version = await self.get_db_version()
104
+ migrations = self.get_migration_files()
105
+
106
+ if not migrations:
107
+ logger.info("No migration files found")
108
+ return current_version
109
+
110
+ if target_version is None:
111
+ target_version = max(m[0] for m in migrations)
112
+
113
+ logger.info(f"Current version: {current_version}, Target: {target_version}")
114
+
115
+ if current_version >= target_version:
116
+ logger.info("Database is up to date")
117
+ return current_version
118
+
119
+ applied = 0
120
+ async with aiosqlite.connect(self.db_path) as db:
121
+ await db.execute("PRAGMA foreign_keys = ON;")
122
+
123
+ for version, filepath in migrations:
124
+ if version <= current_version or version > target_version:
125
+ continue
126
+
127
+ try:
128
+ logger.info(f"Applying migration v{version:03d}: {os.path.basename(filepath)}")
129
+
130
+ with open(filepath, 'r', encoding='utf-8') as f:
131
+ sql_content = f.read()
132
+
133
+ await db.executescript(sql_content)
134
+ await self.set_db_version(version)
135
+ applied += 1
136
+
137
+ logger.info(f"Successfully applied migration v{version:03d}")
138
+
139
+ except Exception as e:
140
+ logger.error(f"Migration v{version:03d} failed: {e}")
141
+ raise RuntimeError(f"Migration failed at version {version}: {e}")
142
+
143
+ final_version = await self.get_db_version()
144
+ logger.info(f"Applied {applied} migrations. Final version: {final_version}")
145
+ return final_version
146
+
147
+
148
+ class DatabaseManager:
149
+ """Database connection and session management"""
150
+
151
+ def __init__(self, database_url: str = None):
152
+ """Initialize database manager
153
+
154
+ Args:
155
+ database_url: Database connection URL. Defaults to SQLite if not provided.
156
+ """
157
+ from .. import shared_state
158
+ self.database_url = database_url or os.getenv(
159
+ 'VIBESURF_DATABASE_URL',
160
+ f'sqlite+aiosqlite:///{os.path.join(shared_state.workspace_dir, "vibe_surf.db")}'
161
+ )
162
+
163
+ # Configure engine based on database type
164
+ if self.database_url.startswith('sqlite'):
165
+ # SQLite configuration for development
166
+ self.engine = create_async_engine(
167
+ self.database_url,
168
+ poolclass=StaticPool,
169
+ connect_args={
170
+ "check_same_thread": False,
171
+ "timeout": 30
172
+ },
173
+ echo=False # Set to True for SQL debugging
174
+ )
175
+ else:
176
+ # PostgreSQL/MySQL configuration for production
177
+ self.engine = create_async_engine(
178
+ self.database_url,
179
+ pool_size=20,
180
+ max_overflow=30,
181
+ pool_pre_ping=True,
182
+ pool_recycle=3600,
183
+ echo=False
184
+ )
185
+
186
+ self.async_session_factory = sessionmaker(
187
+ self.engine,
188
+ class_=AsyncSession,
189
+ expire_on_commit=False
190
+ )
191
+
192
+ # Initialize migration manager for SQLite databases
193
+ if self.database_url.startswith('sqlite'):
194
+ try:
195
+ self.migration_manager = DBMigrationManager(db_path=self.database_url)
196
+ except Exception as e:
197
+ logger.warning(f"Failed to initialize migration manager: {e}")
198
+ self.migration_manager = None
199
+ else:
200
+ self.migration_manager = None
201
+ logger.info("Migration manager is only supported for SQLite databases")
202
+
203
+ async def create_tables(self, use_migrations: bool = True):
204
+ """Create all database tables
205
+
206
+ Args:
207
+ use_migrations: If True, use migration system. If False, use direct table creation.
208
+ """
209
+ if use_migrations and self.migration_manager:
210
+ logger.info("🔄 Using migration system to initialize database...")
211
+ try:
212
+ await self.migration_manager.apply_migrations()
213
+ logger.info("✅ Database initialized via migrations")
214
+ return
215
+ except Exception as e:
216
+ logger.warning(f"Migration failed, falling back to direct table creation: {e}")
217
+
218
+ # Fallback to direct table creation
219
+ logger.info("🔄 Using direct table creation...")
220
+ async with self.engine.begin() as conn:
221
+ await conn.run_sync(Base.metadata.create_all)
222
+ logger.info("✅ Database initialized via direct table creation")
223
+
224
+ async def drop_tables(self):
225
+ """Drop all database tables"""
226
+ async with self.engine.begin() as conn:
227
+ await conn.run_sync(Base.metadata.drop_all)
228
+
229
+ async def get_session(self) -> AsyncGenerator[AsyncSession, None]:
230
+ """Get async database session"""
231
+ async with self.async_session_factory() as session:
232
+ try:
233
+ yield session
234
+ await session.commit()
235
+ except Exception:
236
+ await session.rollback()
237
+ raise
238
+ finally:
239
+ await session.close()
240
+
241
+ async def apply_migrations(self, target_version: Optional[int] = None) -> int:
242
+ """Apply database migrations
243
+
244
+ Args:
245
+ target_version: Target migration version. If None, applies all available migrations.
246
+
247
+ Returns:
248
+ Final database version after applying migrations.
249
+
250
+ Raises:
251
+ RuntimeError: If migration manager is not available or migration fails.
252
+ """
253
+ if not self.migration_manager:
254
+ raise RuntimeError("Migration manager is not available. Only SQLite databases support migrations.")
255
+
256
+ return await self.migration_manager.apply_migrations(target_version)
257
+
258
+ async def get_db_version(self) -> int:
259
+ """Get current database version
260
+
261
+ Returns:
262
+ Current database version, or 0 if not available.
263
+ """
264
+ if not self.migration_manager:
265
+ logger.warning("Migration manager not available, cannot get database version")
266
+ return 0
267
+
268
+ return await self.migration_manager.get_db_version()
269
+
270
+ async def close(self):
271
+ """Close database connections"""
272
+ await self.engine.dispose()
273
+
274
+
275
+ # Dependency for FastAPI
276
+ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
277
+ """FastAPI dependency for database sessions"""
278
+ from .. import shared_state
279
+
280
+ if not shared_state.db_manager:
281
+ raise RuntimeError("Database manager not initialized. Call initialize_vibesurf_components() first.")
282
+
283
+ async for session in shared_state.db_manager.get_session():
284
+ yield session
285
+
286
+
287
+ # Database initialization script
288
+ async def init_database():
289
+ """Initialize database with tables"""
290
+ from .. import shared_state
291
+
292
+ logger.info("🗄️ Initializing VibeSurf database...")
293
+
294
+ try:
295
+ if not shared_state.db_manager:
296
+ raise RuntimeError("Database manager not initialized. Call initialize_vibesurf_components() first.")
297
+
298
+ await shared_state.db_manager.create_tables()
299
+ logger.info("✅ Database tables created successfully")
300
+ logger.info("✅ VibeSurf database ready for single-task execution")
301
+
302
+ except Exception as e:
303
+ logger.error(f"❌ Database initialization failed: {e}")
304
+ raise
305
+
306
+
307
+ if __name__ == "__main__":
308
+ # For standalone execution, initialize a temporary db_manager
309
+ import os
310
+ from .. import shared_state
311
+
312
+ workspace_dir = os.getenv("VIBESURF_WORKSPACE", os.path.join(os.path.dirname(__file__), "../vibesurf_workspace"))
313
+ database_url = os.getenv(
314
+ 'VIBESURF_DATABASE_URL',
315
+ f'sqlite+aiosqlite:///{os.path.join(workspace_dir, "vibe_surf.db")}'
316
+ )
317
+ shared_state.db_manager = DatabaseManager(database_url)
318
+ asyncio.run(init_database())