| name | gui-act |
| description | Execute GUI actions โ click, type, send messages. Includes detection, memory matching, component saving, execution, diff, and transition recording as one unified flow. |
Act โ Detect, Match, Save, Execute, Diff, Record
This is the core action loop. Every action follows this flow. Do not skip any part.
The Complete Action Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. DETECT: Screenshot โ OCR + GPA-GUI-Detector โ
โ 2. MATCH: Compare detected elements against saved memory โ
โ 3. SAVE COMPONENTS: New elements โ crop + save + label โ
โ โ Save BEFORE clicking โ even if click fails, โ
โ components are in memory for next time โ
โ 4. DECIDE & EXECUTE: Pick target โ click/type at coordinates โ
โ 5. DETECT AGAIN: Screenshot โ OCR (only if action might fail) โ
โ 6. DIFF: Compare before vs after OCR texts โ
โ 7. SAVE TRANSITION: Record state change to transitions.json โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key change from previous version: Component saving (step 3) happens BEFORE execution (step 4), not after. This means:
- Even if the click fails, you've already saved what you learned about the current page
- The next visit to this page can use template matching immediately
- You never "lose" detected components by skipping saves after action
Automation API
Two platform-independent functions handle ALL saving automatically.
They work on any screenshot (local Mac, remote VM, downloaded image).
The LLM does NOT manually crop or write JSON files โ call these functions instead.
learn_from_screenshot(img_path, domain, app_name, page_name)
Runs GPA-GUI-Detector + OCR on a screenshot, crops all components, saves to memory.
Call this ONCE per page state you observe (step 3).
from scripts.app_memory import learn_from_screenshot
result = learn_from_screenshot(
img_path="/path/to/screenshot.png",
domain="united.com",
app_name="chromium",
page_name="homepage",
)
record_page_transition(before_img, after_img, click_label, click_pos, domain, app_name)
Runs OCR on before/after screenshots, computes diff, saves state transition.
Call this ONCE per click (step 7).
from scripts.app_memory import record_page_transition
result = record_page_transition(
before_img_path="/path/to/before.png",
after_img_path="/path/to/after.png",
click_label="Travel_info",
click_pos=(779, 187),
domain="united.com",
app_name="chromium",
)
Step-by-Step Walkthrough
Step 1: DETECT (before action)
Take a screenshot. Run OCR + GPA-GUI-Detector on it:
from scripts.ui_detector import detect_text, detect_icons
ocr_results = detect_text(screenshot_path)
icon_results = detect_icons(screenshot_path)
For remote VMs: download screenshot to Mac first, then run detection locally.
Step 2: MATCH against saved memory
Check if components are already in memory:
from scripts.app_memory import match_all_components
matched = match_all_components(app_name, img=screenshot_path, threshold=0.8)
If components match: coordinates come from template matching (most precise). Skip to step 4.
If components are NEW: coordinates come from OCR/GPA-GUI-Detector. Continue to step 3.
Step 3: SAVE COMPONENTS (before clicking!)
Call learn_from_screenshot() to save all detected components automatically.
from scripts.app_memory import learn_from_screenshot
learn_from_screenshot(
img_path=screenshot_path,
domain="united.com",
page_name="homepage",
)
This is automated โ no manual cropping, no manual JSON editing.
The function handles: detection, filtering, naming, dedup, cropping, saving.
Step 4: DECIDE & EXECUTE
Pick the target element, get coordinates from detection (step 1) or memory (step 2), click.
Local Mac apps:
from scripts.app_memory import click_and_record, click_component
click_component(app_name, component_name)
click_and_record(app_name, "Travel_info", 779, 187)
Remote VMs (OSWorld):
import pyautogui
pyautogui.click(779, 187)
CRITICAL: Always use gui_action.py click (with appropriate --remote if needed), never raw platform-specific calls.
Step 5: DETECT AGAIN (if needed)
Take another screenshot after the action. Run OCR to verify the result.
This step is needed when:
- You need to verify the click worked (page changed)
- You need to find the next element to click
- The action might have failed (wrong element, popup appeared)
For simple keyboard shortcuts (Ctrl+L, typing text), you can skip this step.
Step 6: DIFF
Compare OCR texts from before and after screenshots:
- Appeared: new text = new page/state
- Disappeared: gone text = left previous state
- Persisted: unchanged text = persistent UI (nav bar, etc.)
This is done automatically by record_page_transition() in step 7.
Step 7: SAVE TRANSITION
Call record_page_transition() to save the state change automatically.
from scripts.app_memory import record_page_transition
record_page_transition(
before_img_path=before_screenshot,
after_img_path=after_screenshot,
click_label="Travel_info",
click_pos=(779, 187),
domain="united.com",
)
This automatically: runs OCR on both images, diffs them, saves states + transition to states.json / transitions.json.
Concrete Example: OSWorld Task
from scripts.ui_detector import ImageContext, detect_all
elements = detect_all("screenshot.png")
learn_from_screenshot("screenshot.png", domain="united.com", page_name="homepage")
ctx = ImageContext.remote()
click_x, click_y = ctx.image_to_click(779, 187)
pyautogui.click(click_x, click_y)
new_elements = detect_all("new_screenshot.png")
record_page_transition("screenshot.png", "new_screenshot.png",
click_label="Travel_info", click_pos=(779, 187),
domain="united.com")
The Payoff
First visit to united.com:
Screenshot โ GPA-GUI-Detector + OCR โ learn_from_screenshot() saves everything
โ click โ record_page_transition() saves state change
โ Total: ~5 seconds of detection, everything in memory
Second visit to united.com:
Screenshot โ template match against saved components โ instant recognition
โ "I see Travel_info at (661, 188), Bags at (485, 324)"
โ click directly. No GPA. No image tool. Fast.
How Coordinates Work
detect_all() returns image pixel coordinates. Use ImageContext to convert to click-space:
from scripts.ui_detector import ImageContext
ctx = ImageContext.remote()
ctx = ImageContext.mac_fullscreen()
ctx = ImageContext.mac_window(wx, wy)
click_x, click_y = ctx.image_to_click(el["cx"], el["cy"])
| Source | Method | Returns |
|---|
| Saved component | Template matching (match_all_components) | Click-space (already converted) |
| Text element | OCR via detect_all() | Image pixels โ use ctx.image_to_click() |
| UI component | GPA via detect_all() | Image pixels โ use ctx.image_to_click() |
| image tool | NEVER for coordinates | Understanding only |
Not Found?
Component not matching (conf < 0.8) = not on screen in its saved form.
Don't lower threshold. Run learn_from_screenshot() on current page to discover what IS on screen.
Input Methods (gui_action.py)
All GUI operations go through gui_action.py. Add --remote URL for remote targets.
gui_action.py click X Y
gui_action.py right_click X Y
gui_action.py type "text"
gui_action.py key enter
gui_action.py shortcut ctrl+s
gui_action.py screenshot /tmp/s.png
gui_action.py focus "window title"
gui_action.py close "window title"
gui_action.py list_windows
REPORT โ Track Task Performance
Call gui-report at the START and END of every gui-agent workflow (not per-click, per-task).
TRACKER="python3 ~/.openclaw/workspace/skills/gui-agent/skills/gui-report/scripts/tracker.py"
$TRACKER start --task "OSWorld Task 25: United Airlines baggage calculator" --context 94000
$TRACKER tick image_calls
$TRACKER report --context 120000
See gui-report/SKILL.md for details.
โ ABSOLUTE RULES โ Coordinate Sources
โ
ALLOWED coordinate sources:
1. GPA-GUI-Detector (detect_icons) โ bounding box center
2. OCR (detect_text) โ text bounding box center
3. Template matching โ saved component position
โ FORBIDDEN:
- LLM/vision model guessing coordinates
- Hardcoded pixel positions from memory or documentation
- Coordinates from image tool analysis (image tool = understanding ONLY)
Every click: screenshot โ detect โ get coordinates from detection โ click. No exceptions.
Key Principles
- Vision-driven โ screenshot โ detect โ match โ click
- Coordinates from detection only โ image tool is for understanding, NOT coordinates
- Not found = not on screen โ re-learn, don't guess
- State graph drives navigation โ each click records a transition
- First time: screenshot + image. Repeat: detection only โ saves tokens
- Paste > Type for CJK text
- Integer logical coordinates โ use detect_to_click() for Retina
- ALWAYS save to memory โ every GUI operation saves to memory/apps/