Linux GUI agent
943
A Docker image that runs a full Xfce desktop with a VLM-powered GUI automation agent, exposed via A2A and OpenAI-compatible HTTPS APIs, and a noVNC browser viewer for visual debugging.
Implements an agent using the ReAct (Reasoning + Acting) pattern for intelligent desktop GUI automation.
The container bundles:
The agent observes the desktop through screenshots, reasons about the next action, and executes it — repeating until the task is complete or the step budget is exhausted.
| Feature | Details |
|---|---|
| Desktop automation | Full X11 control — click, double-click, drag, scroll, type, key combos, window management |
| VLM-driven agent | Any OpenAI-compatible VLM (GPT-4o, Claude, Gemini, local models) |
| UI Locator model | Optional secondary model (UI-TARS) resolves natural-language element descriptions to pixel coordinates |
| Loop detection | Warns the VLM after 2 consecutive identical actions |
| SSE progress updates | Per-step status messages streamed to A2A clients (VLM call, decision, action execution) |
| A2A API | Google Agent-to-Agent JSON-RPC 2.0 protocol |
| OpenAI-compatible API | /v1/chat/completions endpoint — drop-in for OpenAI clients |
| HTTPS | Self-signed TLS certificate generated at startup |
| Trajectory recording | Saves per-step screenshots, VLM requests/responses, and locator logs |
| Visual debugging | TigerVNC + noVNC web client for watching the agent live |
| Flatpak | Flathub remote pre-configured; install GUI apps with flatpak install -y flathub <app-id> |
cd apps/linux_gui_agent
docker build \
--build-context a2a_pkg=../../packages/a2a_agent \
-f Dockerfile.ubuntu \
-t linux_gui_agent:ubuntu .
docker run -d \
--name gui_agent \
-p 443:443 \
-p 6901:6901 \
-p 5901:5901 \
-e VLM_URL=https://api.openai.com/v1 \
-e VLM_API_KEY=sk-... \
-e VLM_MODEL=gpt-4o \
linux_gui_agent:ubuntu --wait
Build and run a desktop-only container for manually testing X11 tools:
docker build \
--build-context a2a_pkg=../../packages/a2a_agent \
-f Dockerfile.dev \
-t linux_gui_agent:dev .
docker run -d \
--name gui_agent_dev \
-p 5901:5901 \
-p 6901:6901 \
-p 6001:6001 \
linux_gui_agent:dev --wait
| Service | URL / Address |
|---|---|
| Agent API (A2A + OpenAI) | https://localhost/ |
| noVNC web viewer | http://localhost:6901/?password=vncpassword |
| VNC native client | localhost:5901 (password: vncpassword) |
The container generates a local CA on first start. Install it once and your browser will trust all future connections without warnings.
macOS:
curl -k https://localhost/ca.crt -o linux-gui-agent-ca.crt
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain linux-gui-agent-ca.crt
Linux (Debian/Ubuntu):
curl -k https://localhost/ca.crt -o /usr/local/share/ca-certificates/linux-gui-agent-ca.crt
sudo update-ca-certificates
Windows:
curl -k https://localhost/ca.crt -o linux-gui-agent-ca.crt
certutil -addstore Root linux-gui-agent-ca.crt
Chrome / Firefox (manual): download https://localhost/ca.crt then add it in
Settings → Privacy and security → Manage certificates → Authorities → Import.
curl -sk -X POST https://localhost/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"message": {
"role": "user",
"messageId": "m1",
"parts": [{"kind": "text", "text": "Open Chrome, go to example.com, and tell me the page title"}]
}
}
}'
curl -sk -X POST https://localhost/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "desktop-agent",
"messages": [{"role": "user", "content": "Open a terminal and run ls /"}]
}'
curl -sk https://localhost/.well-known/agent-card.json
curl -sk -N -X POST https://localhost/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/stream",
"params": {
"message": {
"role": "user",
"messageId": "m1",
"parts": [{"kind": "text", "text": "Take a screenshot and describe the desktop"}]
}
}
}'
| Variable | Default | Description |
|---|---|---|
VLM_URL | https://api.openai.com/v1 | Base URL of the VLM API |
VLM_API_KEY | — | API key for the VLM |
VLM_MODEL | gpt-4o | Model name |
API_KEY | — | Bearer token to protect the agent API (optional) |
PORT | 443 | HTTPS port for the agent server |
AGENT_URL | — | Public URL published in the AgentCard (e.g. https://my-agent.example.com/). Defaults to https://localhost:{PORT}/ |
DISPLAY | :1 | X11 display used by the agent |
VIEWPORT_WIDTH | 1920 | Desktop width in pixels |
VIEWPORT_HEIGHT | 1080 | Desktop height in pixels |
SEND_IMAGE | true | Set to false for text-only LLMs (disables screenshot in VLM prompt) |
TRAJECTORY_DIR | — | Directory to save trajectory data; each run creates a YYYYMMDD_HHMMSS/ subdirectory |
The locator is a secondary vision model (e.g. UI-TARS) specialised in resolving natural-language element descriptions to exact pixel coordinates. When configured, the agent describes elements by name (query) instead of estimating x/y coordinates, and the locator finds the exact click target.
| Variable | Default | Description |
|---|---|---|
LOCATOR_URL | — | OpenAI-compatible API base URL for the locator model |
LOCATOR_API_KEY | — | Bearer token for locator API auth (optional) |
LOCATOR_MODEL | ui-tars | Model name passed to the locator API |
| Variable | Default | Description |
|---|---|---|
VNC_PW | vncpassword | VNC password |
VNC_RESOLUTION | 1920x1080 | Screen resolution |
| Application | Command | Purpose |
|---|---|---|
| Chromium | chromium-browser & | Web browser (--no-sandbox included automatically) |
| xfce4-terminal | xfce4-terminal & | Terminal emulator |
| mousepad | mousepad & | Text editor |
| Thunar | thunar & | File manager |
| Evince | evince & | PDF viewer |
| Ristretto | ristretto & | Image viewer |
| File-Roller | file-roller & | Archive manager |
| galculator | galculator & | Calculator |
Package managers available: apt-get and flatpak (Flathub configured). snap is not available.
When TRAJECTORY_DIR is set, each agent run creates a timestamped subdirectory containing one folder per step:
$TRAJECTORY_DIR/
20260313_181523/ # run timestamp (YYYYMMDD_HHMMSS)
summary.md # task, result, and final plan summary
turn_0000/
screenshot.png # desktop state at start of step
llm_0_request.json # messages sent to the VLM
llm_0_response.json # raw VLM reply + parsed action/args/reasoning
locator.json # locator model request + response (when LOCATOR_URL is set)
turn_0001/
...
20260313_182041/ # next run
...
Example:
docker run -d \
-e VLM_URL=... -e VLM_API_KEY=... \
-e TRAJECTORY_DIR=/data/trajectory \
-v /tmp/agent-logs:/data/trajectory \
linux_gui_agent:ubuntu --wait
| Tool | Description |
|---|---|
screenshot | Full-screen JPEG screenshot (base64) — taken automatically at every step |
mouse_click | Click at (x, y) with left/right/middle button |
mouse_double_click | Double-click at (x, y) |
mouse_move | Move mouse to (x, y) (hover) |
mouse_scroll | Scroll at (x, y); positive scroll_y = down, negative = up |
key_press | Press a key or modifier combo: Return, Escape, ctrl+c, alt+F4, etc. |
type_text | Type Unicode text into the currently focused element |
drag | Click-drag from (x1, y1) to (x2, y2) |
get_windows | List all open windows with id, title, position, and size |
focus_window | Bring a window to front by window id |
run_command | Run a shell command in the desktop session (360 s timeout; background GUI apps with &) |
get_cursor_pos | Return the current mouse cursor position |
┌──────────────────────────────────────────────┐
│ Docker Container │
│ │
│ Xfce desktop (DISPLAY :1) │
│ Chromium browser │
│ xfce4-terminal, mousepad, evince │
│ flatpak + Flathub │
│ │ │
│ X11Client (python-xlib + wmctrl + xdotool) │
│ │
│ LangGraph (graph.py) │
│ screenshot ──► X11Client.screenshot() │
│ call_vlm ──► VLM (OpenAI-compatible) │
│ execute_action │
│ │ Locator model (optional) │
│ generate_summary │
│ │ │
│ TigerVNC (:5901) + noVNC (:6901) │
│ │
│ FastAPI Server (:443 HTTPS) │
│ ├── A2A router (POST /) │
│ └── OpenAI router (/v1/...) │
└──────────────────────────────────────────────┘
MIT
The agent image exposes an MCP (Model Context Protocol) server on the same port as A2A at /mcp. Any MCP client — Claude Desktop, Cursor, Windsurf, ChatGPT Connectors, OpenAI Agents SDK — can list and invoke these tools using the pod's API_KEY as Bearer.
In production, Core proxies https://linux-gui-agent.agents.forfetch.ai/mcp → the worker pod's /mcp.
| Tool (skill_id) | Description |
|---|---|
take_screenshot | Take a screenshot of the desktop |
click | Click at coordinates or on an element |
double_click | Double-click at coordinates or on an element |
type | Type text using keyboard input |
scroll | Scroll using the mouse wheel |
key_press | Press a keyboard key or key combination |
mouse_move | Move the mouse cursor to coordinates |
Input schemas are authored in apps/agents_mcp/app/seed.py. Skills without an explicit schema advertise a single {task: str} freeform parameter.
Content type
Image
Digest
sha256:a9195f69a…
Size
803.8 MB
Last updated
5 months ago
docker pull superbizon007/linux-gui-agent