Skip to content

Conversation

@savagelysubtle
Copy link

@savagelysubtle savagelysubtle commented Nov 26, 2025

This PR modernizes the browser-use-web-ui project with significant enhancements including UV backend integration, enhanced MCP support, improved UI components, Windows-optimized setup, and modern Python tooling.

Summary by cubic
Modernizes the project with UV-based Python 3.14t, deeper MCP support, and a Windows-optimized setup, plus foundations for event-driven execution and observability. Adds a Deep Research agent, workflow visualization, expanded LLM support, and Docker/noVNC previews for faster onboarding and better runtime feedback.

New Features

UV backend with pyproject.toml and uv.lock; Docker now uses python:3.14-slim, includes docker-compose with VNC/noVNC, and a multi-arch GHCR build workflow.
VS Code debug/settings and tasks for UV sync, Playwright install, and running the WebUI.
MCP integration: MCP_CONFIG_PATH, config loader/validator, mcp.example.json, Settings tab, default path at ./data/mcp.json, and compatibility with langchain-mcp-adapters 0.0.9+.
LLM support expanded with new providers/endpoints and presets (OpenAI incl. O3-mini, Anthropic, Google, DeepSeek/R1, Mistral, Ollama, IBM, Alibaba, Moonshot, Grok); DEFAULT_LLM env added.
UI upgrades: Quick Start tab, rich chat formatting, real-time progress, and improved error messages.
Architecture foundations: event bus + plugin interface; observability with tracer and LLM cost calculator; workflow visualization via a graph builder and simple Gradio visualizer.
Migration

Update imports to src/web_ui/... and remove old src/browser and src/controller paths.
Provide an MCP config via MCP_CONFIG_PATH or ./data/mcp.json (gitignored); configs now require "transport": "stdio" (see mcp.example.json).
Set up with UV: uv python install 3.14t; uv venv --python 3.14t; uv sync; then playwright install chromium --with-deps.
Rebuild Docker images or use docker-compose; expose 6080 (noVNC), 5901 (VNC), and 9222 (CDP).
On Windows, prefer setup-windows.ps1 or setup-windows.bat for a guided install.


Summary by cubic

Modernizes the Web UI with a UV-based Python backend, deeper MCP integration, expanded LLM support, and a Windows/Docker-optimized setup. Adds a new Deep Research agent, event-driven foundations, and improved UI/observability for faster onboarding and clearer runtime feedback.

  • New Features

    • UV backend with pyproject.toml/uv.lock; Python 3.14-slim Docker; docker-compose with VNC/noVNC; multi-arch GHCR build workflow.
    • Enhanced MCP integration: MCP_CONFIG_PATH, config loader/validator, Settings tab, mcp.example.json, default at ./data/mcp.json.
    • Expanded LLM support and presets (OpenAI incl. O3-mini, Anthropic, Google, DeepSeek/R1, Mistral, Ollama, IBM, Alibaba, Moonshot, Grok); DEFAULT_LLM env.
    • New Deep Research agent (LangGraph), event bus + plugin interface, tracer and LLM cost calculator, workflow visualization.
    • UI upgrades: Quick Start, agent tabs, rich chat formatting, real-time progress/errors, VS Code debug/tasks for UV and Playwright.
  • Migration

    • Update imports to src/web_ui/...; remove old src/browser, src/controller, and related modules.
    • Provide MCP config via MCP_CONFIG_PATH or ./data/mcp.json with "transport": "stdio".
    • Set up with UV: uv python install 3.14t; uv venv --python 3.14t; uv sync; playwright install chromium --with-deps.
    • Rebuild Docker or use docker-compose; expose 7788 (app), 6080 (noVNC), 5901 (VNC), 9222 (CDP).
    • On Windows, use setup-windows.ps1 or setup-windows.bat; update .env with new endpoints and DEFAULT_LLM.

Written for commit a84e164. Summary will update automatically on new commits.

vvincent1234 and others added 30 commits January 27, 2025 16:36
…_browser-use

Fix/adapt latest browser use
…_browser-use

Fix/adapt latest browser use
refactor: remove code duplication in get_next_action method
Modified Dockerfile and Dockerfile.arm64 to use port 5901 for VNC service
maintaining consistency with supervisord.conf and docker-compose.yml
configurations.
- Restore noVNC program section in supervisord.conf for web-based preview
- Fix port mappings in docker-compose.yml for proper VNC access
- Ensure correct configuration for web-based browser interaction preview
massage -> message
This PR introduces proper error handling in the UI for missing API keys, addressing issue browser-use#188. Previously, missing API keys resulted in tracebacks in the console, but no clear error was shown in the UI. This made it difficult to understand what went wrong. Now, API key checks are performed at the start of the `get_llm_model` function in `src/utils/utils.py`. I've also added a `PROVIDER_DISPLAY_NAMES` constant for more user-friendly error messages and a `handle_api_key_error` function that leverages `gr.Error` to display clear error messages directly in the UI. If an API key is missing, you'll now see an error message right away. The `run_browser_agent` and `run_with_stream` functions in `webui.py` have been adjusted to ensure these `gr.Error` exceptions are handled properly.
…idate-llm-tests

refactor: simplify LLM tests and remove duplication
…-api-key-errors

fix: display missing API key errors in UI
Update README.md minor grammar update
README states the default VNC password is `vncpassword` but it is actually `youvncpassword` in the env file
Features/mistralai intergrate
- Add O3-mini model to OpenAI model options
- Remove forced dark theme to allow system/light theme options
- Fix JSON template escape sequence in custom prompts
…odel-support

feat: add OpenAI O3-mini model support and fix theme handling
savagelysubtle and others added 26 commits October 21, 2025 17:12
- Create format_error_message() function with smart error detection
- Add context-aware error suggestions based on error type
- Implement collapsible traceback for technical details
- Add rich CSS styling for error containers
- Include helpful suggestions for common errors (API keys, connections, models, etc.)
- Replace basic error messages with formatted versions
- Add visual hierarchy with icons and color coding

This is the third quick win from Phase 1 (Real-time UX improvements)
- Create comprehensive workflow graph builder with node/edge tracking
- Define NodeType enum (start, thinking, action, result, error, end)
- Define NodeStatus enum (pending, running, completed, error, skipped)
- Implement WorkflowNode dataclass with timing and position tracking
- Implement WorkflowEdge dataclass for connections between nodes
- Add automatic layout calculation with vertical spacing
- Include action icon mapping for visual clarity
- Sanitize sensitive parameters (passwords, tokens, keys)
- Add duration tracking for each node execution
- Support export to dict/JSON for Gradio integration
- Format code with Ruff (fix formatting from previous commits)

This is the foundation for Phase 2 (Visual Workflow Builder)
- Create workflow_visualizer.py for JSON-based workflow display
- Implement create_workflow_visualizer() for UI components
- Add format_workflow_for_display() for readable workflow data
- Add generate_workflow_status_markdown() for status summaries
- Include custom CSS for workflow styling
- Format workflow as timeline with icons and status indicators
- Display progress, duration, and step details

This provides a simple workflow visualization using Gradio's built-in components
**Core Tracing System:**
- Create trace_models.py with TraceSpan and ExecutionTrace dataclasses
- Implement SpanType enum (AGENT_RUN, LLM_CALL, TOOL_CALL, BROWSER_ACTION, RETRIEVAL)
- Add span lifecycle management (complete, error_out)
- Support nested span hierarchies with parent_id tracking
- Track execution metrics (duration, tokens, cost)
- Aggregate trace-level metrics automatically

**Tracer Implementation:**
- Create AgentTracer with async context manager for spans
- Implement span stack for nested execution tracking
- Add start_trace and end_trace lifecycle methods
- Support real-time span creation during execution
- Include comprehensive logging for debugging

**Cost Calculator:**
- Add LLM_PRICING dictionary with 20+ model prices
- Support OpenAI, Anthropic, Google, DeepSeek, Mistral models
- Calculate costs per 1M tokens (input + output)
- Implement fuzzy model name matching
- Add estimate_task_cost for pre-execution planning
- Format costs for display with appropriate precision

**Key Features:**
- Zero-configuration tracing (automatic instrumentation)
- Rich metadata support (inputs, outputs, tags)
- Cost tracking per LLM call
- Trace summaries and analytics
- Export to dict/JSON for persistence
- Separate getters for LLM spans, action spans, failed spans

This provides LangSmith-level observability for agent execution.

Code is fully type-hinted, documented, and passes all linter checks.
**Event Bus Infrastructure:**
- Create EventType enum with 25+ event types covering:
  - Agent lifecycle (start, step, complete, error, paused, resumed)
  - LLM operations (request, token streaming, response, error)
  - Browser actions (start, complete, error, navigate, screenshot)
  - Trace events (span start/end, trace complete)
  - UI events (connected, disconnected, commands)
  - Workflow events (node start/complete, edge traversal)
- Implement Event dataclass with correlation IDs for tracing
- Build EventBus with pub/sub pattern
- Support both in-memory and Redis backends
- Async event handling with error isolation
- Background event processing queue
- Global event bus singleton pattern

**Plugin System:**
- Create PluginManifest dataclass for plugin metadata
- Define Plugin abstract base class
- Support plugin capabilities:
  - Custom browser actions
  - UI component extensions
  - Event handler registration
  - Configuration schemas
- Add plugin lifecycle (initialize, shutdown)
- Define plugin exceptions (PluginError, PluginLoadError, etc.)
- Export plugin info and configuration

**Key Features:**
- Decoupled architecture via events
- Scalable with Redis backend option
- Type-safe event and plugin interfaces
- Comprehensive error handling
- Async-first design
- Zero-overhead when not using Redis

**Code Quality:**
- Full type hints with collections.abc
- Comprehensive docstrings
- Clean separation of concerns
- Logger integration
- Environment-based configuration

This provides the foundation for event-driven, plugin-extensible architecture.
The Accordion layout component was incorrectly being stored in tab_components
and used as an output, which caused InvalidComponentError. Accordions are
layout-only and cannot be used as inputs/outputs.

Changes:
- Remove 'as summary_accordion' from Accordion declaration
- Remove 'summary_accordion' from tab_components dictionary
- Keep only the server_summary Markdown component as output

This fixes the Gradio InvalidComponentError when loading MCP settings.
- Introduced a new markdown file for testing sequential thinking capabilities of BrowserUseAgent and DeepResearchAgent.
- Defined specific tasks for each agent, outlining expected reasoning steps and logging behavior.
- Provided instructions on how to run the tests and check logs for tool usage.

This enhances the testing framework for agent reasoning processes.
Create detailed status report covering all 4 enhancement phases:

**Report Sections:**
- Executive summary with 50% completion status
- Technology stack implementation status
- Phase-by-phase feature tracking with metrics
- Architecture overview with file structure
- Database schema status (defined, not implemented)
- Code quality metrics (3,400+ lines, 0 errors)
- Performance targets and current status
- Security implementation checklist
- Remaining work breakdown with time estimates
- Integration checklist
- Testing status and recommendations
- Deployment readiness assessment
- Success metrics and targets

**Key Highlights:**
- 12 of 24 features complete (50%)
- Zero technical debt
- Production-ready foundations
- 8-12 weeks estimated for completion

**Phase Completion:**
- Phase 1: 50% (3/6 features)
- Phase 2: 67% (4/6 features)
- Phase 3: 50% (3/6 features)
- Phase 4: 33% (2/6 features)

This document serves as the single source of truth for project status
and provides clear roadmap for completion.
…anced navigation

- Introduced a new Quick Start tab featuring dynamic status display, preset configurations, and a user-friendly interface.
- Improved overall navigation structure by reorganizing tabs and enhancing CSS styling for better visual hierarchy.
- Implemented fully functional buttons for preset configurations, allowing users to quickly apply settings.
- Enhanced user experience with collapsible accordions in settings, providing a cleaner interface and reducing clutter.
- Maintained backward compatibility while delivering a modern, intuitive design.

This update significantly improves onboarding time and overall usability for both new and experienced users.
…ling

- Updated the MCP tool registration logic to support individual server configurations.
- Implemented retrieval of tools for each server based on the provided server configuration.
- Improved logging to reflect the total number of tools registered across all servers.
- Ensured compatibility with the new langchain-mcp-adapters 0.1.0+ API.

This change optimizes the registration process and enhances the flexibility of tool management.
- Added .env.local, .env.development, and .env.production to the .gitignore file.
- This change helps prevent sensitive environment configuration files from being tracked in version control.

Enhances project security and maintains clean repository management.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
- Deleted multiple planning documents related to the enhancement overview, phases, technical specifications, deployment guide, and testing strategy.
- Removed associated images that were no longer needed for the project.

This cleanup helps streamline the repository and focuses on the current implementation needs.
- Added '.claude/' to the .gitignore file to prevent tracking of generated files.
- Retained existing entry for 'workflow' to maintain clean repository management.

This change helps ensure that unnecessary files are not included in version control.
…ed user experience

- Added a new dashboard component consolidating agent selection, task input, and control buttons for both Browser Use Agent and Deep Research Agent.
- Implemented a collapsible settings panel with configurations for LLM, Browser, and MCP, allowing for streamlined user adjustments.
- Enhanced UI with responsive design, improved navigation, and integrated help modal for user guidance.
- Introduced new components for status display, quick presets, and task history, improving overall usability and accessibility.
- Established a robust event-driven architecture for managing interactions and state across the dashboard.

This update significantly enhances the user interface and experience, making it easier for users to manage tasks and configurations effectively.
- Updated PowerShell commands for terminating processes on ports 7788 and 8080 to use a more efficient method for retrieving the process ID.
- Replaced the previous method of handling multiple lines with a streamlined approach that selects the first matching line and splits it for the process ID extraction.

This change enhances the reliability and clarity of the process termination logic in the development environment.
- Deleted the BACKEND_IMPROVEMENTS.md, DEBUG_SETTINGS_BUTTON.md, SESSION_SUMMARY.md, and test_dashboard.py files as they are no longer relevant to the current project structure and implementation.
- This cleanup helps streamline the repository and focuses on maintaining only necessary and up-to-date documentation and test cases.
- Commented out the previous entry for 'data/' to reflect its new usage for settings.
- Added specific ignore rules for the 'data/' directory, allowing only '.gitkeep' and 'README.md' to be tracked.
- Updated ignore patterns for '.claude/' to prevent tracking of generated files.

These changes enhance repository cleanliness and ensure proper management of configuration files.
…directories

- Added entries for '.claude/' and '.cursor/' to prevent tracking of IDE and editor configuration files.
- This change helps maintain a clean repository by excluding unnecessary files from version control.
removing all .claude from pull request
deleting all .claude from pull request
…tool

- Updated MCP configuration paths to use `data/mcp.json` instead of the root directory, ensuring better organization of settings.
- Added a new diagnostic tool (`diagnose_dropdown_issue.py`) to assist in troubleshooting LLM provider dropdown issues, enhancing user support.
- Created a `data` directory with a README to document its purpose and structure for persistent settings.
- Improved the settings management in the Web UI, including migration of old settings and default settings loading.
- Enhanced logging and error handling for MCP client setup and model dropdown updates, improving overall reliability.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
…e_dropdown_issue.py`) as it is no longer needed. This cleanup helps streamline the repository by eliminating obsolete files.
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
8 out of 9 committers have signed the CLA.

✅ dhavalDev123
✅ balaboom123
✅ ntsd
✅ yrk111222
✅ ngocanhnt269
✅ savagelysubtle
✅ Akash-ainapur
✅ odaysec
❌ warmshao
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

40 issues found across 85 files

Prompt for AI agents (all 40 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/web_ui/utils/mcp_client.py">

<violation number="1" location="src/web_ui/utils/mcp_client.py:53">
`tool.args_schema` is a Pydantic model class, so treating it like a dict causes `create_tool_param_model` to raise `TypeError` for any tool that defines an args schema.</violation>
</file>

<file name="src/web_ui/webui/components/chat_formatter.py">

<violation number="1" location="src/web_ui/webui/components/chat_formatter.py:23">
Agent messages are emitted as HTML without escaping, so any `&lt;script&gt;` or markup in `content` will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.</violation>

<violation number="2" location="src/web_ui/webui/components/chat_formatter.py:120">
Escape code block contents before embedding them in `&lt;code&gt;` to prevent XSS and preserve literal rendering.</violation>
</file>

<file name="src/web_ui/webui/interface.py">

<violation number="1" location="src/web_ui/webui/interface.py:710">
run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.</violation>

<violation number="2" location="src/web_ui/webui/interface.py:710">
run_research_wrapper yields fewer values than declared outputs, so starting the Deep Research agent immediately crashes with a Gradio output-count error.</violation>

<violation number="3" location="src/web_ui/webui/interface.py:710">
submit_help_wrapper streams only the partial dict values while the click handler declares every component as an output, so pressing “Submit Help” raises a Gradio output-count error instead of delivering the help text.</violation>

<violation number="4" location="src/web_ui/webui/interface.py:722">
clear_wrapper wraps the entire dict of component updates as the single chatbot output, so the Clear button throws and all the intended resets (run/stop/pause buttons, browser view, etc.) never occur.</violation>

<violation number="5" location="src/web_ui/webui/interface.py:782">
Deep-research Stop handler returns UI updates while the click event declares zero outputs, so stopping the agent throws an output-count error and the UI never gets reset.</violation>
</file>

<file name="supervisord.conf">

<violation number="1" location="supervisord.conf:42">
`depends_on` is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.</violation>

<violation number="2" location="supervisord.conf:54">
This `depends_on` directive is not supported by Supervisor, so keeping it here causes Supervisor to exit with a config error.</violation>
</file>

<file name="src/web_ui/webui/components/load_save_config_tab.py">

<violation number="1" location="src/web_ui/webui/components/load_save_config_tab.py:13">
`gr.File` needs `type=&quot;filepath&quot;` so `load_config` receives a path string instead of an UploadedFile object, otherwise `open(config_path)` fails at runtime.</violation>

<violation number="2" location="src/web_ui/webui/components/load_save_config_tab.py:40">
`save_config_wrapper` passes a dictionary to `WebuiManager.save_config`, which expects positional component values; this causes the save logic to build an invalid settings payload and throw during JSON serialization, breaking the Save Config button.</violation>
</file>

<file name="src/web_ui/webui/components/browser_use_agent_tab.py">

<violation number="1" location="src/web_ui/webui/components/browser_use_agent_tab.py:247">
Ask-for-assistant requests always fail because the code checks a non-existent `_chat_history` attribute instead of `bu_chat_history`.</violation>
</file>

<file name="pyproject.toml">

<violation number="1" location="pyproject.toml:52">
The new console script points to the top-level `webui` module, but package discovery only includes `src/web_ui*`, so the referenced module is not shipped and the installed entry point will crash with `ModuleNotFoundError`.</violation>
</file>

<file name="src/web_ui/webui/components/mcp_settings_tab.py">

<violation number="1" location="src/web_ui/webui/components/mcp_settings_tab.py:44">
Custom configuration path is used without restriction, allowing the MCP Settings UI to read or overwrite arbitrary JSON files anywhere on the server.</violation>
</file>

<file name="src/web_ui/agent/deep_research/deep_research_agent.py">

<violation number="1" location="src/web_ui/agent/deep_research/deep_research_agent.py:733">
Returning the task dict instead of state updates prevents the graph from advancing or saving progress when the LLM makes no tool calls, causing the research workflow to stall.</violation>

<violation number="2" location="src/web_ui/agent/deep_research/deep_research_agent.py:1237">
Using startswith to enforce that save_dir stays under ./tmp/deep_research is insecure—paths like `/tmp/deep_research_evil` pass the check and escape the sandbox.</violation>
</file>

<file name="src/web_ui/webui/components/dashboard_settings.py">

<violation number="1" location="src/web_ui/webui/components/dashboard_settings.py:461">
Removing the agent_settings component registration leaves existing tabs that still fetch agent_settings.* IDs without any registered components, causing KeyErrors when those tabs load.</violation>
</file>

<file name="tests/test_agents.py">

<violation number="1" location="tests/test_agents.py:24">
This test creates a live OpenAI client without checking for `OPENAI_API_KEY`, and `llm_provider.get_llm_model` raises when the key is missing. In CI the secret isn’t available, so the entire test suite fails before running the body. Add a skip guard or mock when the env var is absent.</violation>

<violation number="2" location="tests/test_agents.py:139">
Ensure test_browser_use_parallel re-raises caught exceptions so automation detects failures.</violation>

<violation number="3" location="tests/test_agents.py:139">
Let exceptions from test_browser_use_agent propagate (or re-raise after logging) so the test fails when the agent does.</violation>

<violation number="4" location="tests/test_agents.py:201">
The Azure integration test relies on real Azure OpenAI credentials and doesn’t guard against missing `AZURE_OPENAI_API_KEY`, so it crashes during client construction in environments without that secret. Mark it to skip or inject a fake client when the env isn’t configured so the suite can run.</violation>

<violation number="5" location="tests/test_agents.py:294">
Remove the pdb.set_trace() breakpoint so automated test runs do not hang waiting for a debugger.</violation>

<violation number="6" location="tests/test_agents.py:315">
Deep research test fails immediately when `OPENAI_API_KEY` isn’t set because the OpenAI client creation raises. Add a skip/mocking strategy so the test suite doesn’t require real secrets to run.</violation>

<violation number="7" location="tests/test_agents.py:377">
Allow exceptions in test_deep_research_agent to propagate (or re-raise after logging) so the test meaningfully fails on errors.</violation>
</file>

<file name="src/web_ui/browser/custom_browser.py">

<violation number="1" location="src/web_ui/browser/custom_browser.py:99">
Launching Firefox or WebKit now fails because `channel=&quot;chromium&quot;` is passed unconditionally; Playwright only accepts a channel for Chromium browsers, so non-Chromium launches raise an error.</violation>
</file>

<file name="docker-compose.yml">

<violation number="1" location="docker-compose.yml:65">
The compose file exposes VNC/noVNC ports but hard-codes the password to a known string (`youvncpassword`), leaving the container’s desktop wide open when run with defaults. Require the operator to supply their own secret instead of shipping a public one.</violation>
</file>

<file name=".github/workflows/build.yml">

<violation number="1" location=".github/workflows/build.yml:39">
`secrets.GITHUB_TOKEN` is used to push to GHCR without granting the workflow `packages: write` permission, so docker/login-action and pushes will be denied.</violation>

<violation number="2" location=".github/workflows/build.yml:124">
`docker buildx imagetools inspect` always receives an empty tag because DOCKER_METADATA_OUTPUT_VERSION is only set when no tags exist, causing the manifest job to fail on every normal push.</violation>
</file>

<file name="src/web_ui/webui/components/dashboard_sidebar.py">

<violation number="1" location="src/web_ui/webui/components/dashboard_sidebar.py:175">
Recent task titles/descriptions are inserted into raw HTML without escaping, enabling XSS if a task contains markup.</violation>
</file>

<file name="tests/test_controller.py">

<violation number="1" location="tests/test_controller.py:133">
Using time.sleep inside the async loop blocks the event loop and prevents other coroutines from running. Replace it with await asyncio.sleep so the test remains cooperative.</violation>
</file>

<file name="tests/test_llm_api.py">

<violation number="1" location="tests/test_llm_api.py:72">
Remove the `pdb.set_trace()` breakpoint in the DeepSeek Ollama branch so the test suite can run unattended.</violation>
</file>

<file name="src/web_ui/webui/components/deep_research_agent_tab.py">

<violation number="1" location="src/web_ui/webui/components/deep_research_agent_tab.py:395">
The Stop handler references a non-existent component (`deep_research_agent.max_iteration`), so pressing Stop without an active task raises KeyError instead of resetting the controls.</violation>
</file>

<file name="src/web_ui/observability/trace_models.py">

<violation number="1" location="src/web_ui/observability/trace_models.py:138">
Serializing the trace sets `final_output` to `None` whenever the actual result is falsy (e.g., `0`, `False`, `&quot;&quot;`), losing valid agent outputs.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:74">
`playwright install --with-deps` is Linux-only; including it in the Windows setup instructions causes the install command to fail.</violation>

<violation number="2" location="README.md:77">
`playwright install chromium --with-deps` is unsupported on Windows, so the documented command fails and blocks browser installation.</violation>
</file>

<file name="src/web_ui/controller/custom_controller.py">

<violation number="1" location="src/web_ui/controller/custom_controller.py:70">
`upload_file` crashes when `available_file_paths` falls back to its documented default of `None`. Guard against `None` before checking membership so file uploads can execute without the optional list.</violation>
</file>

<file name="Dockerfile">

<violation number="1" location="Dockerfile:81">
The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.</violation>
</file>

<file name="src/web_ui/webui/webui_manager.py">

<violation number="1" location="src/web_ui/webui/webui_manager.py:194">
load_default_settings assigns the entire Component object to `comp.value`, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.</violation>
</file>

<file name="src/web_ui/utils/llm_provider.py">

<violation number="1" location="src/web_ui/utils/llm_provider.py:285">
Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

"""Creates a Pydantic model from a LangChain tool's schema"""

# Get tool schema information
json_schema = tool.args_schema
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tool.args_schema is a Pydantic model class, so treating it like a dict causes create_tool_param_model to raise TypeError for any tool that defines an args schema.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/utils/mcp_client.py, line 53:

<comment>`tool.args_schema` is a Pydantic model class, so treating it like a dict causes `create_tool_param_model` to raise `TypeError` for any tool that defines an args schema.</comment>

<file context>
@@ -0,0 +1,257 @@
+    &quot;&quot;&quot;Creates a Pydantic model from a LangChain tool&#39;s schema&quot;&quot;&quot;
+
+    # Get tool schema information
+    json_schema = tool.args_schema
+    tool_name = tool.name
+
</file context>

language = match.group(1) or ""
code = match.group(2)
lang_class = f' class="language-{language}"' if language else ""
return f"<pre><code{lang_class}>{code}</code></pre>"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Escape code block contents before embedding them in <code> to prevent XSS and preserve literal rendering.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/components/chat_formatter.py, line 120:

<comment>Escape code block contents before embedding them in `&lt;code&gt;` to prevent XSS and preserve literal rendering.</comment>

<file context>
@@ -0,0 +1,611 @@
+        language = match.group(1) or &quot;&quot;
+        code = match.group(2)
+        lang_class = f&#39; class=&quot;language-{language}&quot;&#39; if language else &quot;&quot;
+        return f&quot;&lt;pre&gt;&lt;code{lang_class}&gt;{code}&lt;/code&gt;&lt;/pre&gt;&quot;
+
+    return re.sub(pattern, replace_code_block, text, flags=re.DOTALL)
</file context>

if not content:
return ""

formatted = content
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent messages are emitted as HTML without escaping, so any <script> or markup in content will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/components/chat_formatter.py, line 23:

<comment>Agent messages are emitted as HTML without escaping, so any `&lt;script&gt;` or markup in `content` will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.</comment>

<file context>
@@ -0,0 +1,611 @@
+    if not content:
+        return &quot;&quot;
+
+    formatted = content
+
+    # Add action badge if action metadata is present
</file context>

"""Wrapper for run_agent_task that yields updates."""
components_dict = dict(zip(ui_manager.get_components(), args, strict=True))
async for update_dict in run_agent_task(ui_manager, components_dict):
yield list(update_dict.values())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/interface.py, line 710:

<comment>run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.</comment>

<file context>
@@ -0,0 +1,802 @@
+            &quot;&quot;&quot;Wrapper for run_agent_task that yields updates.&quot;&quot;&quot;
+            components_dict = dict(zip(ui_manager.get_components(), args, strict=True))
+            async for update_dict in run_agent_task(ui_manager, components_dict):
+                yield list(update_dict.values())
+
+        async def stop_wrapper():
</file context>
Suggested change
yield list(update_dict.values())
yield [update_dict.get(comp, gr.update()) for comp in ui_manager.get_components()]

startsecs=10
stopsignal=TERM
stopwaitsecs=10
depends_on=vnc_setup,xvfb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depends_on is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supervisord.conf, line 42:

<comment>`depends_on` is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.</comment>

<file context>
@@ -0,0 +1,80 @@
+startsecs=10
+stopsignal=TERM
+stopwaitsecs=10
+depends_on=vnc_setup,xvfb
+
+[program:x11vnc_log]
</file context>

uv sync
# Install Playwright browsers
playwright install --with-deps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

playwright install --with-deps is Linux-only; including it in the Windows setup instructions causes the install command to fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 74:

<comment>`playwright install --with-deps` is Linux-only; including it in the Windows setup instructions causes the install command to fail.</comment>

<file context>
@@ -1,52 +1,196 @@
+uv sync
+
+# Install Playwright browsers
+playwright install --with-deps
+
+# Or install specific browser
</file context>
Suggested change
playwright install --with-deps
playwright install

async def upload_file(
index: int, path: str, browser: BrowserContext, available_file_paths: list[str]
):
if path not in available_file_paths:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upload_file crashes when available_file_paths falls back to its documented default of None. Guard against None before checking membership so file uploads can execute without the optional list.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/controller/custom_controller.py, line 70:

<comment>`upload_file` crashes when `available_file_paths` falls back to its documented default of `None`. Guard against `None` before checking membership so file uploads can execute without the optional list.</comment>

<file context>
@@ -0,0 +1,276 @@
+        async def upload_file(
+            index: int, path: str, browser: BrowserContext, available_file_paths: list[str]
+        ):
+            if path not in available_file_paths:
+                return ActionResult(error=f&quot;File path {path} is not available&quot;)
+
</file context>
Suggested change
if path not in available_file_paths:
if not available_file_paths or path not in available_file_paths:

UV_LINK_MODE=copy

# Install Python dependencies using UV
RUN uv pip install --system -r requirements.txt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Dockerfile, line 81:

<comment>The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.</comment>

<file context>
@@ -0,0 +1,102 @@
+    UV_LINK_MODE=copy
+
+# Install Python dependencies using UV
+RUN uv pip install --system -r requirements.txt
+
+# Playwright setup
</file context>


# Apply updates without yielding (blocking update)
for comp, val in update_components.items():
comp.value = val
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_default_settings assigns the entire Component object to comp.value, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/webui_manager.py, line 194:

<comment>load_default_settings assigns the entire Component object to `comp.value`, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.</comment>

<file context>
@@ -0,0 +1,320 @@
+
+                # Apply updates without yielding (blocking update)
+                for comp, val in update_components.items():
+                    comp.value = val
+
+                # Manually trigger provider change to update model dropdown
</file context>
Suggested change
comp.value = val
comp.value = val.value

model_name=kwargs.get("model_name", "moonshot-v1-32k-vision-preview"),
temperature=kwargs.get("temperature", 0.0),
openai_api_base=os.getenv("MOONSHOT_ENDPOINT"),
openai_api_key=SecretStr(os.getenv("MOONSHOT_API_KEY") or ""),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/utils/llm_provider.py, line 285:

<comment>Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.</comment>

<file context>
@@ -0,0 +1,326 @@
+            model_name=kwargs.get(&quot;model_name&quot;, &quot;moonshot-v1-32k-vision-preview&quot;),
+            temperature=kwargs.get(&quot;temperature&quot;, 0.0),
+            openai_api_base=os.getenv(&quot;MOONSHOT_ENDPOINT&quot;),
+            openai_api_key=SecretStr(os.getenv(&quot;MOONSHOT_API_KEY&quot;) or &quot;&quot;),
+        )
+    elif provider == &quot;unbound&quot;:
</file context>
Suggested change
openai_api_key=SecretStr(os.getenv("MOONSHOT_API_KEY") or ""),
openai_api_key=SecretStr(api_key),

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

40 issues found across 85 files

Prompt for AI agents (all 40 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/web_ui/utils/mcp_client.py">

<violation number="1" location="src/web_ui/utils/mcp_client.py:53">
`tool.args_schema` is a Pydantic model class, so treating it like a dict causes `create_tool_param_model` to raise `TypeError` for any tool that defines an args schema.</violation>
</file>

<file name="src/web_ui/webui/components/chat_formatter.py">

<violation number="1" location="src/web_ui/webui/components/chat_formatter.py:23">
Agent messages are emitted as HTML without escaping, so any `&lt;script&gt;` or markup in `content` will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.</violation>

<violation number="2" location="src/web_ui/webui/components/chat_formatter.py:120">
Escape code block contents before embedding them in `&lt;code&gt;` to prevent XSS and preserve literal rendering.</violation>
</file>

<file name="src/web_ui/webui/interface.py">

<violation number="1" location="src/web_ui/webui/interface.py:710">
run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.</violation>

<violation number="2" location="src/web_ui/webui/interface.py:710">
run_research_wrapper yields fewer values than declared outputs, so starting the Deep Research agent immediately crashes with a Gradio output-count error.</violation>

<violation number="3" location="src/web_ui/webui/interface.py:710">
submit_help_wrapper streams only the partial dict values while the click handler declares every component as an output, so pressing “Submit Help” raises a Gradio output-count error instead of delivering the help text.</violation>

<violation number="4" location="src/web_ui/webui/interface.py:722">
clear_wrapper wraps the entire dict of component updates as the single chatbot output, so the Clear button throws and all the intended resets (run/stop/pause buttons, browser view, etc.) never occur.</violation>

<violation number="5" location="src/web_ui/webui/interface.py:782">
Deep-research Stop handler returns UI updates while the click event declares zero outputs, so stopping the agent throws an output-count error and the UI never gets reset.</violation>
</file>

<file name="supervisord.conf">

<violation number="1" location="supervisord.conf:42">
`depends_on` is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.</violation>

<violation number="2" location="supervisord.conf:54">
This `depends_on` directive is not supported by Supervisor, so keeping it here causes Supervisor to exit with a config error.</violation>
</file>

<file name="src/web_ui/webui/components/load_save_config_tab.py">

<violation number="1" location="src/web_ui/webui/components/load_save_config_tab.py:13">
`gr.File` needs `type=&quot;filepath&quot;` so `load_config` receives a path string instead of an UploadedFile object, otherwise `open(config_path)` fails at runtime.</violation>

<violation number="2" location="src/web_ui/webui/components/load_save_config_tab.py:40">
`save_config_wrapper` passes a dictionary to `WebuiManager.save_config`, which expects positional component values; this causes the save logic to build an invalid settings payload and throw during JSON serialization, breaking the Save Config button.</violation>
</file>

<file name="src/web_ui/webui/components/browser_use_agent_tab.py">

<violation number="1" location="src/web_ui/webui/components/browser_use_agent_tab.py:247">
Ask-for-assistant requests always fail because the code checks a non-existent `_chat_history` attribute instead of `bu_chat_history`.</violation>
</file>

<file name="pyproject.toml">

<violation number="1" location="pyproject.toml:52">
The new console script points to the top-level `webui` module, but package discovery only includes `src/web_ui*`, so the referenced module is not shipped and the installed entry point will crash with `ModuleNotFoundError`.</violation>
</file>

<file name="src/web_ui/webui/components/mcp_settings_tab.py">

<violation number="1" location="src/web_ui/webui/components/mcp_settings_tab.py:44">
Custom configuration path is used without restriction, allowing the MCP Settings UI to read or overwrite arbitrary JSON files anywhere on the server.</violation>
</file>

<file name="src/web_ui/agent/deep_research/deep_research_agent.py">

<violation number="1" location="src/web_ui/agent/deep_research/deep_research_agent.py:733">
Returning the task dict instead of state updates prevents the graph from advancing or saving progress when the LLM makes no tool calls, causing the research workflow to stall.</violation>

<violation number="2" location="src/web_ui/agent/deep_research/deep_research_agent.py:1237">
Using startswith to enforce that save_dir stays under ./tmp/deep_research is insecure—paths like `/tmp/deep_research_evil` pass the check and escape the sandbox.</violation>
</file>

<file name="src/web_ui/webui/components/dashboard_settings.py">

<violation number="1" location="src/web_ui/webui/components/dashboard_settings.py:461">
Removing the agent_settings component registration leaves existing tabs that still fetch agent_settings.* IDs without any registered components, causing KeyErrors when those tabs load.</violation>
</file>

<file name="tests/test_agents.py">

<violation number="1" location="tests/test_agents.py:24">
This test creates a live OpenAI client without checking for `OPENAI_API_KEY`, and `llm_provider.get_llm_model` raises when the key is missing. In CI the secret isn’t available, so the entire test suite fails before running the body. Add a skip guard or mock when the env var is absent.</violation>

<violation number="2" location="tests/test_agents.py:139">
Ensure test_browser_use_parallel re-raises caught exceptions so automation detects failures.</violation>

<violation number="3" location="tests/test_agents.py:139">
Let exceptions from test_browser_use_agent propagate (or re-raise after logging) so the test fails when the agent does.</violation>

<violation number="4" location="tests/test_agents.py:201">
The Azure integration test relies on real Azure OpenAI credentials and doesn’t guard against missing `AZURE_OPENAI_API_KEY`, so it crashes during client construction in environments without that secret. Mark it to skip or inject a fake client when the env isn’t configured so the suite can run.</violation>

<violation number="5" location="tests/test_agents.py:294">
Remove the pdb.set_trace() breakpoint so automated test runs do not hang waiting for a debugger.</violation>

<violation number="6" location="tests/test_agents.py:315">
Deep research test fails immediately when `OPENAI_API_KEY` isn’t set because the OpenAI client creation raises. Add a skip/mocking strategy so the test suite doesn’t require real secrets to run.</violation>

<violation number="7" location="tests/test_agents.py:377">
Allow exceptions in test_deep_research_agent to propagate (or re-raise after logging) so the test meaningfully fails on errors.</violation>
</file>

<file name="src/web_ui/browser/custom_browser.py">

<violation number="1" location="src/web_ui/browser/custom_browser.py:99">
Launching Firefox or WebKit now fails because `channel=&quot;chromium&quot;` is passed unconditionally; Playwright only accepts a channel for Chromium browsers, so non-Chromium launches raise an error.</violation>
</file>

<file name="docker-compose.yml">

<violation number="1" location="docker-compose.yml:65">
The compose file exposes VNC/noVNC ports but hard-codes the password to a known string (`youvncpassword`), leaving the container’s desktop wide open when run with defaults. Require the operator to supply their own secret instead of shipping a public one.</violation>
</file>

<file name=".github/workflows/build.yml">

<violation number="1" location=".github/workflows/build.yml:39">
`secrets.GITHUB_TOKEN` is used to push to GHCR without granting the workflow `packages: write` permission, so docker/login-action and pushes will be denied.</violation>

<violation number="2" location=".github/workflows/build.yml:124">
`docker buildx imagetools inspect` always receives an empty tag because DOCKER_METADATA_OUTPUT_VERSION is only set when no tags exist, causing the manifest job to fail on every normal push.</violation>
</file>

<file name="src/web_ui/webui/components/dashboard_sidebar.py">

<violation number="1" location="src/web_ui/webui/components/dashboard_sidebar.py:175">
Recent task titles/descriptions are inserted into raw HTML without escaping, enabling XSS if a task contains markup.</violation>
</file>

<file name="tests/test_controller.py">

<violation number="1" location="tests/test_controller.py:133">
Using time.sleep inside the async loop blocks the event loop and prevents other coroutines from running. Replace it with await asyncio.sleep so the test remains cooperative.</violation>
</file>

<file name="tests/test_llm_api.py">

<violation number="1" location="tests/test_llm_api.py:72">
Remove the `pdb.set_trace()` breakpoint in the DeepSeek Ollama branch so the test suite can run unattended.</violation>
</file>

<file name="src/web_ui/webui/components/deep_research_agent_tab.py">

<violation number="1" location="src/web_ui/webui/components/deep_research_agent_tab.py:395">
The Stop handler references a non-existent component (`deep_research_agent.max_iteration`), so pressing Stop without an active task raises KeyError instead of resetting the controls.</violation>
</file>

<file name="src/web_ui/observability/trace_models.py">

<violation number="1" location="src/web_ui/observability/trace_models.py:138">
Serializing the trace sets `final_output` to `None` whenever the actual result is falsy (e.g., `0`, `False`, `&quot;&quot;`), losing valid agent outputs.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:74">
`playwright install --with-deps` is Linux-only; including it in the Windows setup instructions causes the install command to fail.</violation>

<violation number="2" location="README.md:77">
`playwright install chromium --with-deps` is unsupported on Windows, so the documented command fails and blocks browser installation.</violation>
</file>

<file name="src/web_ui/controller/custom_controller.py">

<violation number="1" location="src/web_ui/controller/custom_controller.py:70">
`upload_file` crashes when `available_file_paths` falls back to its documented default of `None`. Guard against `None` before checking membership so file uploads can execute without the optional list.</violation>
</file>

<file name="Dockerfile">

<violation number="1" location="Dockerfile:81">
The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.</violation>
</file>

<file name="src/web_ui/webui/webui_manager.py">

<violation number="1" location="src/web_ui/webui/webui_manager.py:194">
load_default_settings assigns the entire Component object to `comp.value`, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.</violation>
</file>

<file name="src/web_ui/utils/llm_provider.py">

<violation number="1" location="src/web_ui/utils/llm_provider.py:285">
Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

"""Creates a Pydantic model from a LangChain tool's schema"""

# Get tool schema information
json_schema = tool.args_schema
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tool.args_schema is a Pydantic model class, so treating it like a dict causes create_tool_param_model to raise TypeError for any tool that defines an args schema.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/utils/mcp_client.py, line 53:

<comment>`tool.args_schema` is a Pydantic model class, so treating it like a dict causes `create_tool_param_model` to raise `TypeError` for any tool that defines an args schema.</comment>

<file context>
@@ -0,0 +1,257 @@
+    &quot;&quot;&quot;Creates a Pydantic model from a LangChain tool&#39;s schema&quot;&quot;&quot;
+
+    # Get tool schema information
+    json_schema = tool.args_schema
+    tool_name = tool.name
+
</file context>

language = match.group(1) or ""
code = match.group(2)
lang_class = f' class="language-{language}"' if language else ""
return f"<pre><code{lang_class}>{code}</code></pre>"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Escape code block contents before embedding them in <code> to prevent XSS and preserve literal rendering.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/components/chat_formatter.py, line 120:

<comment>Escape code block contents before embedding them in `&lt;code&gt;` to prevent XSS and preserve literal rendering.</comment>

<file context>
@@ -0,0 +1,611 @@
+        language = match.group(1) or &quot;&quot;
+        code = match.group(2)
+        lang_class = f&#39; class=&quot;language-{language}&quot;&#39; if language else &quot;&quot;
+        return f&quot;&lt;pre&gt;&lt;code{lang_class}&gt;{code}&lt;/code&gt;&lt;/pre&gt;&quot;
+
+    return re.sub(pattern, replace_code_block, text, flags=re.DOTALL)
</file context>

if not content:
return ""

formatted = content
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent messages are emitted as HTML without escaping, so any <script> or markup in content will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/components/chat_formatter.py, line 23:

<comment>Agent messages are emitted as HTML without escaping, so any `&lt;script&gt;` or markup in `content` will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.</comment>

<file context>
@@ -0,0 +1,611 @@
+    if not content:
+        return &quot;&quot;
+
+    formatted = content
+
+    # Add action badge if action metadata is present
</file context>

"""Wrapper for run_agent_task that yields updates."""
components_dict = dict(zip(ui_manager.get_components(), args, strict=True))
async for update_dict in run_agent_task(ui_manager, components_dict):
yield list(update_dict.values())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/interface.py, line 710:

<comment>run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.</comment>

<file context>
@@ -0,0 +1,802 @@
+            &quot;&quot;&quot;Wrapper for run_agent_task that yields updates.&quot;&quot;&quot;
+            components_dict = dict(zip(ui_manager.get_components(), args, strict=True))
+            async for update_dict in run_agent_task(ui_manager, components_dict):
+                yield list(update_dict.values())
+
+        async def stop_wrapper():
</file context>
Suggested change
yield list(update_dict.values())
yield [update_dict.get(comp, gr.update()) for comp in ui_manager.get_components()]

startsecs=10
stopsignal=TERM
stopwaitsecs=10
depends_on=vnc_setup,xvfb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depends_on is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supervisord.conf, line 42:

<comment>`depends_on` is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.</comment>

<file context>
@@ -0,0 +1,80 @@
+startsecs=10
+stopsignal=TERM
+stopwaitsecs=10
+depends_on=vnc_setup,xvfb
+
+[program:x11vnc_log]
</file context>

uv sync
# Install Playwright browsers
playwright install --with-deps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

playwright install --with-deps is Linux-only; including it in the Windows setup instructions causes the install command to fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 74:

<comment>`playwright install --with-deps` is Linux-only; including it in the Windows setup instructions causes the install command to fail.</comment>

<file context>
@@ -1,52 +1,196 @@
+uv sync
+
+# Install Playwright browsers
+playwright install --with-deps
+
+# Or install specific browser
</file context>
Suggested change
playwright install --with-deps
playwright install

async def upload_file(
index: int, path: str, browser: BrowserContext, available_file_paths: list[str]
):
if path not in available_file_paths:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upload_file crashes when available_file_paths falls back to its documented default of None. Guard against None before checking membership so file uploads can execute without the optional list.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/controller/custom_controller.py, line 70:

<comment>`upload_file` crashes when `available_file_paths` falls back to its documented default of `None`. Guard against `None` before checking membership so file uploads can execute without the optional list.</comment>

<file context>
@@ -0,0 +1,276 @@
+        async def upload_file(
+            index: int, path: str, browser: BrowserContext, available_file_paths: list[str]
+        ):
+            if path not in available_file_paths:
+                return ActionResult(error=f&quot;File path {path} is not available&quot;)
+
</file context>
Suggested change
if path not in available_file_paths:
if not available_file_paths or path not in available_file_paths:

UV_LINK_MODE=copy

# Install Python dependencies using UV
RUN uv pip install --system -r requirements.txt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Dockerfile, line 81:

<comment>The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.</comment>

<file context>
@@ -0,0 +1,102 @@
+    UV_LINK_MODE=copy
+
+# Install Python dependencies using UV
+RUN uv pip install --system -r requirements.txt
+
+# Playwright setup
</file context>


# Apply updates without yielding (blocking update)
for comp, val in update_components.items():
comp.value = val
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_default_settings assigns the entire Component object to comp.value, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/webui_manager.py, line 194:

<comment>load_default_settings assigns the entire Component object to `comp.value`, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.</comment>

<file context>
@@ -0,0 +1,320 @@
+
+                # Apply updates without yielding (blocking update)
+                for comp, val in update_components.items():
+                    comp.value = val
+
+                # Manually trigger provider change to update model dropdown
</file context>
Suggested change
comp.value = val
comp.value = val.value

model_name=kwargs.get("model_name", "moonshot-v1-32k-vision-preview"),
temperature=kwargs.get("temperature", 0.0),
openai_api_base=os.getenv("MOONSHOT_ENDPOINT"),
openai_api_key=SecretStr(os.getenv("MOONSHOT_API_KEY") or ""),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/utils/llm_provider.py, line 285:

<comment>Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.</comment>

<file context>
@@ -0,0 +1,326 @@
+            model_name=kwargs.get(&quot;model_name&quot;, &quot;moonshot-v1-32k-vision-preview&quot;),
+            temperature=kwargs.get(&quot;temperature&quot;, 0.0),
+            openai_api_base=os.getenv(&quot;MOONSHOT_ENDPOINT&quot;),
+            openai_api_key=SecretStr(os.getenv(&quot;MOONSHOT_API_KEY&quot;) or &quot;&quot;),
+        )
+    elif provider == &quot;unbound&quot;:
</file context>
Suggested change
openai_api_key=SecretStr(os.getenv("MOONSHOT_API_KEY") or ""),
openai_api_key=SecretStr(api_key),

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

40 issues found across 85 files

Prompt for AI agents (all 40 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/web_ui/utils/mcp_client.py">

<violation number="1" location="src/web_ui/utils/mcp_client.py:53">
`tool.args_schema` is a Pydantic model class, so treating it like a dict causes `create_tool_param_model` to raise `TypeError` for any tool that defines an args schema.</violation>
</file>

<file name="src/web_ui/webui/components/chat_formatter.py">

<violation number="1" location="src/web_ui/webui/components/chat_formatter.py:23">
Agent messages are emitted as HTML without escaping, so any `&lt;script&gt;` or markup in `content` will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.</violation>

<violation number="2" location="src/web_ui/webui/components/chat_formatter.py:120">
Escape code block contents before embedding them in `&lt;code&gt;` to prevent XSS and preserve literal rendering.</violation>
</file>

<file name="src/web_ui/webui/interface.py">

<violation number="1" location="src/web_ui/webui/interface.py:710">
run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.</violation>

<violation number="2" location="src/web_ui/webui/interface.py:710">
run_research_wrapper yields fewer values than declared outputs, so starting the Deep Research agent immediately crashes with a Gradio output-count error.</violation>

<violation number="3" location="src/web_ui/webui/interface.py:710">
submit_help_wrapper streams only the partial dict values while the click handler declares every component as an output, so pressing “Submit Help” raises a Gradio output-count error instead of delivering the help text.</violation>

<violation number="4" location="src/web_ui/webui/interface.py:722">
clear_wrapper wraps the entire dict of component updates as the single chatbot output, so the Clear button throws and all the intended resets (run/stop/pause buttons, browser view, etc.) never occur.</violation>

<violation number="5" location="src/web_ui/webui/interface.py:782">
Deep-research Stop handler returns UI updates while the click event declares zero outputs, so stopping the agent throws an output-count error and the UI never gets reset.</violation>
</file>

<file name="supervisord.conf">

<violation number="1" location="supervisord.conf:42">
`depends_on` is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.</violation>

<violation number="2" location="supervisord.conf:54">
This `depends_on` directive is not supported by Supervisor, so keeping it here causes Supervisor to exit with a config error.</violation>
</file>

<file name="src/web_ui/webui/components/load_save_config_tab.py">

<violation number="1" location="src/web_ui/webui/components/load_save_config_tab.py:13">
`gr.File` needs `type=&quot;filepath&quot;` so `load_config` receives a path string instead of an UploadedFile object, otherwise `open(config_path)` fails at runtime.</violation>

<violation number="2" location="src/web_ui/webui/components/load_save_config_tab.py:40">
`save_config_wrapper` passes a dictionary to `WebuiManager.save_config`, which expects positional component values; this causes the save logic to build an invalid settings payload and throw during JSON serialization, breaking the Save Config button.</violation>
</file>

<file name="src/web_ui/webui/components/browser_use_agent_tab.py">

<violation number="1" location="src/web_ui/webui/components/browser_use_agent_tab.py:247">
Ask-for-assistant requests always fail because the code checks a non-existent `_chat_history` attribute instead of `bu_chat_history`.</violation>
</file>

<file name="pyproject.toml">

<violation number="1" location="pyproject.toml:52">
The new console script points to the top-level `webui` module, but package discovery only includes `src/web_ui*`, so the referenced module is not shipped and the installed entry point will crash with `ModuleNotFoundError`.</violation>
</file>

<file name="src/web_ui/webui/components/mcp_settings_tab.py">

<violation number="1" location="src/web_ui/webui/components/mcp_settings_tab.py:44">
Custom configuration path is used without restriction, allowing the MCP Settings UI to read or overwrite arbitrary JSON files anywhere on the server.</violation>
</file>

<file name="src/web_ui/agent/deep_research/deep_research_agent.py">

<violation number="1" location="src/web_ui/agent/deep_research/deep_research_agent.py:733">
Returning the task dict instead of state updates prevents the graph from advancing or saving progress when the LLM makes no tool calls, causing the research workflow to stall.</violation>

<violation number="2" location="src/web_ui/agent/deep_research/deep_research_agent.py:1237">
Using startswith to enforce that save_dir stays under ./tmp/deep_research is insecure—paths like `/tmp/deep_research_evil` pass the check and escape the sandbox.</violation>
</file>

<file name="src/web_ui/webui/components/dashboard_settings.py">

<violation number="1" location="src/web_ui/webui/components/dashboard_settings.py:461">
Removing the agent_settings component registration leaves existing tabs that still fetch agent_settings.* IDs without any registered components, causing KeyErrors when those tabs load.</violation>
</file>

<file name="tests/test_agents.py">

<violation number="1" location="tests/test_agents.py:24">
This test creates a live OpenAI client without checking for `OPENAI_API_KEY`, and `llm_provider.get_llm_model` raises when the key is missing. In CI the secret isn’t available, so the entire test suite fails before running the body. Add a skip guard or mock when the env var is absent.</violation>

<violation number="2" location="tests/test_agents.py:139">
Ensure test_browser_use_parallel re-raises caught exceptions so automation detects failures.</violation>

<violation number="3" location="tests/test_agents.py:139">
Let exceptions from test_browser_use_agent propagate (or re-raise after logging) so the test fails when the agent does.</violation>

<violation number="4" location="tests/test_agents.py:201">
The Azure integration test relies on real Azure OpenAI credentials and doesn’t guard against missing `AZURE_OPENAI_API_KEY`, so it crashes during client construction in environments without that secret. Mark it to skip or inject a fake client when the env isn’t configured so the suite can run.</violation>

<violation number="5" location="tests/test_agents.py:294">
Remove the pdb.set_trace() breakpoint so automated test runs do not hang waiting for a debugger.</violation>

<violation number="6" location="tests/test_agents.py:315">
Deep research test fails immediately when `OPENAI_API_KEY` isn’t set because the OpenAI client creation raises. Add a skip/mocking strategy so the test suite doesn’t require real secrets to run.</violation>

<violation number="7" location="tests/test_agents.py:377">
Allow exceptions in test_deep_research_agent to propagate (or re-raise after logging) so the test meaningfully fails on errors.</violation>
</file>

<file name="src/web_ui/browser/custom_browser.py">

<violation number="1" location="src/web_ui/browser/custom_browser.py:99">
Launching Firefox or WebKit now fails because `channel=&quot;chromium&quot;` is passed unconditionally; Playwright only accepts a channel for Chromium browsers, so non-Chromium launches raise an error.</violation>
</file>

<file name="docker-compose.yml">

<violation number="1" location="docker-compose.yml:65">
The compose file exposes VNC/noVNC ports but hard-codes the password to a known string (`youvncpassword`), leaving the container’s desktop wide open when run with defaults. Require the operator to supply their own secret instead of shipping a public one.</violation>
</file>

<file name=".github/workflows/build.yml">

<violation number="1" location=".github/workflows/build.yml:39">
`secrets.GITHUB_TOKEN` is used to push to GHCR without granting the workflow `packages: write` permission, so docker/login-action and pushes will be denied.</violation>

<violation number="2" location=".github/workflows/build.yml:124">
`docker buildx imagetools inspect` always receives an empty tag because DOCKER_METADATA_OUTPUT_VERSION is only set when no tags exist, causing the manifest job to fail on every normal push.</violation>
</file>

<file name="src/web_ui/webui/components/dashboard_sidebar.py">

<violation number="1" location="src/web_ui/webui/components/dashboard_sidebar.py:175">
Recent task titles/descriptions are inserted into raw HTML without escaping, enabling XSS if a task contains markup.</violation>
</file>

<file name="tests/test_controller.py">

<violation number="1" location="tests/test_controller.py:133">
Using time.sleep inside the async loop blocks the event loop and prevents other coroutines from running. Replace it with await asyncio.sleep so the test remains cooperative.</violation>
</file>

<file name="tests/test_llm_api.py">

<violation number="1" location="tests/test_llm_api.py:72">
Remove the `pdb.set_trace()` breakpoint in the DeepSeek Ollama branch so the test suite can run unattended.</violation>
</file>

<file name="src/web_ui/webui/components/deep_research_agent_tab.py">

<violation number="1" location="src/web_ui/webui/components/deep_research_agent_tab.py:395">
The Stop handler references a non-existent component (`deep_research_agent.max_iteration`), so pressing Stop without an active task raises KeyError instead of resetting the controls.</violation>
</file>

<file name="src/web_ui/observability/trace_models.py">

<violation number="1" location="src/web_ui/observability/trace_models.py:138">
Serializing the trace sets `final_output` to `None` whenever the actual result is falsy (e.g., `0`, `False`, `&quot;&quot;`), losing valid agent outputs.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:74">
`playwright install --with-deps` is Linux-only; including it in the Windows setup instructions causes the install command to fail.</violation>

<violation number="2" location="README.md:77">
`playwright install chromium --with-deps` is unsupported on Windows, so the documented command fails and blocks browser installation.</violation>
</file>

<file name="src/web_ui/controller/custom_controller.py">

<violation number="1" location="src/web_ui/controller/custom_controller.py:70">
`upload_file` crashes when `available_file_paths` falls back to its documented default of `None`. Guard against `None` before checking membership so file uploads can execute without the optional list.</violation>
</file>

<file name="Dockerfile">

<violation number="1" location="Dockerfile:81">
The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.</violation>
</file>

<file name="src/web_ui/webui/webui_manager.py">

<violation number="1" location="src/web_ui/webui/webui_manager.py:194">
load_default_settings assigns the entire Component object to `comp.value`, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.</violation>
</file>

<file name="src/web_ui/utils/llm_provider.py">

<violation number="1" location="src/web_ui/utils/llm_provider.py:285">
Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

"""Creates a Pydantic model from a LangChain tool's schema"""

# Get tool schema information
json_schema = tool.args_schema
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tool.args_schema is a Pydantic model class, so treating it like a dict causes create_tool_param_model to raise TypeError for any tool that defines an args schema.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/utils/mcp_client.py, line 53:

<comment>`tool.args_schema` is a Pydantic model class, so treating it like a dict causes `create_tool_param_model` to raise `TypeError` for any tool that defines an args schema.</comment>

<file context>
@@ -0,0 +1,257 @@
+    &quot;&quot;&quot;Creates a Pydantic model from a LangChain tool&#39;s schema&quot;&quot;&quot;
+
+    # Get tool schema information
+    json_schema = tool.args_schema
+    tool_name = tool.name
+
</file context>
Fix with Cubic

language = match.group(1) or ""
code = match.group(2)
lang_class = f' class="language-{language}"' if language else ""
return f"<pre><code{lang_class}>{code}</code></pre>"
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Escape code block contents before embedding them in <code> to prevent XSS and preserve literal rendering.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/components/chat_formatter.py, line 120:

<comment>Escape code block contents before embedding them in `&lt;code&gt;` to prevent XSS and preserve literal rendering.</comment>

<file context>
@@ -0,0 +1,611 @@
+        language = match.group(1) or &quot;&quot;
+        code = match.group(2)
+        lang_class = f&#39; class=&quot;language-{language}&quot;&#39; if language else &quot;&quot;
+        return f&quot;&lt;pre&gt;&lt;code{lang_class}&gt;{code}&lt;/code&gt;&lt;/pre&gt;&quot;
+
+    return re.sub(pattern, replace_code_block, text, flags=re.DOTALL)
</file context>
Fix with Cubic

if not content:
return ""

formatted = content
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent messages are emitted as HTML without escaping, so any <script> or markup in content will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/components/chat_formatter.py, line 23:

<comment>Agent messages are emitted as HTML without escaping, so any `&lt;script&gt;` or markup in `content` will be executed when rendered. Sanitize/escape the message before wrapping it in badges or other formatting.</comment>

<file context>
@@ -0,0 +1,611 @@
+    if not content:
+        return &quot;&quot;
+
+    formatted = content
+
+    # Add action badge if action metadata is present
</file context>
Fix with Cubic

"""Wrapper for run_agent_task that yields updates."""
components_dict = dict(zip(ui_manager.get_components(), args, strict=True))
async for update_dict in run_agent_task(ui_manager, components_dict):
yield list(update_dict.values())
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/interface.py, line 710:

<comment>run_agent_wrapper only yields as many values as the current update dict, but the click event declares every component as an output, so the run button raises a ValueError and the agent never starts.</comment>

<file context>
@@ -0,0 +1,802 @@
+            &quot;&quot;&quot;Wrapper for run_agent_task that yields updates.&quot;&quot;&quot;
+            components_dict = dict(zip(ui_manager.get_components(), args, strict=True))
+            async for update_dict in run_agent_task(ui_manager, components_dict):
+                yield list(update_dict.values())
+
+        async def stop_wrapper():
</file context>
Suggested change
yield list(update_dict.values())
yield [update_dict.get(comp, gr.update()) for comp in ui_manager.get_components()]
Fix with Cubic

startsecs=10
stopsignal=TERM
stopwaitsecs=10
depends_on=vnc_setup,xvfb
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depends_on is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supervisord.conf, line 42:

<comment>`depends_on` is not a valid Supervisord program option, so this config fails to load as soon as Supervisord encounters it. Remove the option (and rely on priorities or another supported mechanism) so Supervisord can start.</comment>

<file context>
@@ -0,0 +1,80 @@
+startsecs=10
+stopsignal=TERM
+stopwaitsecs=10
+depends_on=vnc_setup,xvfb
+
+[program:x11vnc_log]
</file context>
Fix with Cubic

uv sync
# Install Playwright browsers
playwright install --with-deps
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

playwright install --with-deps is Linux-only; including it in the Windows setup instructions causes the install command to fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 74:

<comment>`playwright install --with-deps` is Linux-only; including it in the Windows setup instructions causes the install command to fail.</comment>

<file context>
@@ -1,52 +1,196 @@
+uv sync
+
+# Install Playwright browsers
+playwright install --with-deps
+
+# Or install specific browser
</file context>
Suggested change
playwright install --with-deps
playwright install
Fix with Cubic

async def upload_file(
index: int, path: str, browser: BrowserContext, available_file_paths: list[str]
):
if path not in available_file_paths:
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upload_file crashes when available_file_paths falls back to its documented default of None. Guard against None before checking membership so file uploads can execute without the optional list.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/controller/custom_controller.py, line 70:

<comment>`upload_file` crashes when `available_file_paths` falls back to its documented default of `None`. Guard against `None` before checking membership so file uploads can execute without the optional list.</comment>

<file context>
@@ -0,0 +1,276 @@
+        async def upload_file(
+            index: int, path: str, browser: BrowserContext, available_file_paths: list[str]
+        ):
+            if path not in available_file_paths:
+                return ActionResult(error=f&quot;File path {path} is not available&quot;)
+
</file context>
Suggested change
if path not in available_file_paths:
if not available_file_paths or path not in available_file_paths:
Fix with Cubic

UV_LINK_MODE=copy

# Install Python dependencies using UV
RUN uv pip install --system -r requirements.txt
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Dockerfile, line 81:

<comment>The Docker build ignores the project’s uv.lock and installs unlocked dependency versions, so container builds can silently drift away from the pinned development environment and break as soon as an upstream release introduces incompatibilities.</comment>

<file context>
@@ -0,0 +1,102 @@
+    UV_LINK_MODE=copy
+
+# Install Python dependencies using UV
+RUN uv pip install --system -r requirements.txt
+
+# Playwright setup
</file context>
Fix with Cubic


# Apply updates without yielding (blocking update)
for comp, val in update_components.items():
comp.value = val
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_default_settings assigns the entire Component object to comp.value, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/webui/webui_manager.py, line 194:

<comment>load_default_settings assigns the entire Component object to `comp.value`, so default settings never populate and Gradio ends up with invalid component values. Assign the stored value instead of the component instance.</comment>

<file context>
@@ -0,0 +1,320 @@
+
+                # Apply updates without yielding (blocking update)
+                for comp, val in update_components.items():
+                    comp.value = val
+
+                # Manually trigger provider change to update model dropdown
</file context>
Suggested change
comp.value = val
comp.value = val.value
Fix with Cubic

model_name=kwargs.get("model_name", "moonshot-v1-32k-vision-preview"),
temperature=kwargs.get("temperature", 0.0),
openai_api_base=os.getenv("MOONSHOT_ENDPOINT"),
openai_api_key=SecretStr(os.getenv("MOONSHOT_API_KEY") or ""),
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/web_ui/utils/llm_provider.py, line 285:

<comment>Moonshot provider discards the validated API key and always reads from the environment, so providing the key via UI/kwargs results in unauthenticated requests.</comment>

<file context>
@@ -0,0 +1,326 @@
+            model_name=kwargs.get(&quot;model_name&quot;, &quot;moonshot-v1-32k-vision-preview&quot;),
+            temperature=kwargs.get(&quot;temperature&quot;, 0.0),
+            openai_api_base=os.getenv(&quot;MOONSHOT_ENDPOINT&quot;),
+            openai_api_key=SecretStr(os.getenv(&quot;MOONSHOT_API_KEY&quot;) or &quot;&quot;),
+        )
+    elif provider == &quot;unbound&quot;:
</file context>
Suggested change
openai_api_key=SecretStr(os.getenv("MOONSHOT_API_KEY") or ""),
openai_api_key=SecretStr(api_key),
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.