← Back to Plugins
Tools

Plugin Ovp Macos

xuyuelun667-alt By xuyuelun667-alt ⭐ 1 stars 👁 63 views ▲ 0 votes

Give text-only OpenClaw agents eyes on macOS: local Apple Vision OCR + Accessibility structure -> compact, priority-ranked Visual State.

GitHub

Install

npm install
npm

README

# OVP — give a text-only agent eyes on macOS

[![CI](https://github.com/xuyuelun667-alt/openclaw-plugin-ovp-macos/actions/workflows/ci.yml/badge.svg)](https://github.com/xuyuelun667-alt/openclaw-plugin-ovp-macos/actions/workflows/ci.yml)
![Platform: macOS](https://img.shields.io/badge/platform-macOS-000000)
![License: MIT](https://img.shields.io/badge/license-MIT-blue)
![Tests](https://img.shields.io/badge/tests-16%20passing-brightgreen)
![Engine deps](https://img.shields.io/badge/engine%20deps-none-brightgreen)

**Your agent can't see. Paste a screenshot and it fails, then burns a dozen tool calls trying to OCR it itself. OVP replaces that whole detour with ~350 tokens of structured text — computed locally, in under half a second, with coordinates.**

An [OpenClaw](https://github.com/openclaw/openclaw) plugin (macOS) that turns a screenshot, a single
window, or the live screen into a **compact, priority-ranked Visual State** using Apple Vision OCR,
`CGWindowList`, and the Accessibility API — and injects it as the agent's image input.

```bash
openclaw plugins install clawhub:@xuyuelun667-alt/ovp-macos
openclaw ovp setup          # wires it up + checks permissions
```

![OVP demo](docs/demo.svg)

---

## The problem, measured

A text-only model receiving a screenshot has nothing to work with. Real runs from building this
plugin (same machine, same model, same image):

| | without OVP | with OVP |
|---|---|---|
| Tool calls for one image question | **11–16**, often without converging | **0** |
| Tokens burned per image task | ≈ +16k in / +10.6k out vs. baseline | +≈0.4k in |
| Task cost | ≈ $0.020 | ≈ $0.0027 |
| Time to a useful answer | tens of seconds of self-derivation | **0.14–0.41 s** |
| Where the image went | possibly a third-party vision API | nowhere — it never leaves the machine |

The saving is not a smaller prompt. It is the **11–16 rounds the agent no longer invents.**

## How it works

![How OVP works](docs/flow.svg)

Capture → metadata → Apple Vision OCR (bbox + confidence) → Accessibility tree → `CGWindowList` →
region segmentation → priority classification → contract renderer. One Swift binary, **no
third-party dependencies, no model weights, no network**.

## What the agent actually receives

Not a description — a contract built for a text-only consumer:

```
You are a text-only model. Do not call view_image. Visual information has been preprocessed by OVP and is provided below.
HEADLINE: source=screenshot_file app=unknown | dialog=0 text=55 shown=23 ui=0 | alert=2 error=1 | truncated=true
ALERT/ERROR (3):
  ![1252,649,333,30] c=0.50 synthetic system dialog for
  ![1449,755,38,34] c=1.00 好
TEXT (55 total, 20 shown):
  [r1/generic_region 316,94,380,30] OpenClaw … · ⑤ … · 127.0.0.1:18789/chat/main/…
MORE: truncated=true hidden=text:28 ui:0 reg:6 dup:0 noise:4 | query: --grep <s> | --region x,y,w,h | --max-chars N | --headline-only
ESCALATE: vlm:possible_dialog | local state is insufficient here; an opt-in vision call can resolve it.
```

Four rules make it work:

1. **The first line removes a guaranteed-failing round.** A text-only model cannot use `view_image`.
   Saying so up front eliminated that wasted call in every measured run.
2. **Priority, not reading order.** `dialog/alert > error > focused element > frontmost-window text >
   result-like > everything else`, with a two-pass classifier so a bare button label (`好`, `OK`)
   only counts as a dialog once dialog/error evidence exists. Alerts are force-included even when the
   budget is exhausted.
3. **Truncation is stated, and the follow-up queries are real.** `truncated`, per-category hidden
   counts, and working `--grep` (returns matches *with their neighbours*), `--region`, `--max-chars`,
   `--headline-only`. Nobody has to guess what was dropped.
4. **Escalation is declared by the engine, not guessed by the model.** Two measured triggers:
   `vlm:possible_dialog` (alert text present but no dialog *structure* — typical for screenshot
   files, which have no Accessibility tree) and `vlm:icon_heavy` (almost no text and almost no
   coverage: the frame is graphics). Both opt-in.

## Compared with a hosted vision model

Same images, same tasks, measured side by side ([MiniMax-VL-01](https://platform.minimax.io)):

| | OVP | hosted VLM |
|---|---|---|
| Cost per screenshot | **$0** | ≈ $0.0105 |
| Latency | **0.14–0.41 s** | 11.8–31.2 s |
| Success rate | **6/6** | 6/8 (dense screenshots timed out; 2/2 retries too) |
| Determinism | same input → same output | two runs differed (similarity 0.564) |
| Field-level accuracy (verifiable set) | 94% / 100% | 88% / 100% |
| Coordinates, Accessibility roles, actions | ✅ | ❌ |
| Works offline | ✅ | ❌ |

Two findings worth the read: the VLM **reconstructed a line that was clipped to a 16-pixel sliver**
(where OVP returned garbage and marked it low-confidence) — it looks like higher accuracy until you
check the pixels. And with an unbounded "list all the text" prompt it **timed out twice** on a dense
screenshot. Use it as an *escalation*, not as the default. That is exactly what `ESCALATE:` is for.

## Install

```bash
# ClawHub (once the release passes review)
openclaw plugins install clawhub:@xuyuelun667-alt/ovp-macos
# npm
openclaw plugins install npm:@xuyuelun667-alt/ovp-macos
```

The package ships a **prebuilt universal engine** (`bin/ovp`, arm64 + x86_64), because
`openclaw plugins install` runs with `--ignore-scripts` and cannot build anything at install time.

<details>
<summary>From source, or to rebuild the engine yourself</summary>

```bash
git clone https://github.com/xuyuelun667-alt/openclaw-plugin-ovp-macos
cd openclaw-plugin-ovp-macos
npm install
npm run build:engine     # universal swift build -> ./bin/ovp (atomic replace)
npm run build            # tsc -> ./dist
npm test                 # 16 unit tests, no network or permissions needed

openclaw plugins install "npm-pack:$(npm pack --silent)" --force --accept-capabilities
```
</details>

## Setup and diagnostics

```bash
openclaw ovp setup              # wire it up (idempotent; --dry-run to preview)
openclaw ovp doctor             # engine + Accessibility / Screen Recording preflight
```

`setup` computes the minimal diff, prints **every write with its reason**, applies it through
`openclaw config set` (so it is validated and audited by the host, exactly as if you typed it), then
re-runs the permission check. On an already wired install it says `already wired — nothing to change`.

<details>
<summary>What it writes (the manual equivalent)</summary>

```bash
openclaw config set agents.defaults.imageModel.primary ovp-macos/ovp-local
openclaw config set tools.media.image.maxChars 1500
openclaw config set tools.media.image.preferredModel ovp-macos
openclaw config set tools.alsoAllow '["visual_inspect"]'
openclaw daemon restart
```
</details>

### Permissions (where macOS plugins usually die silently)

Two grants, given to **the process that runs OpenClaw** (the Gateway/node binary) — child processes
inherit them, so `ovp` never needs its own entry:

| Grant | Needed for | Without it |
|---|---|---|
| **Accessibility** | control roles/titles/actions, focused element | control reads are empty (`ui=0`) |
| **Screen Recording** | window list with names, `screencapture` | unnamed window rows; blank captures |

`openclaw ovp doctor` (or the `visual_inspect` tool with `mode=doctor`) tells you exactly which one is
missing and where to enable it.

### Pitfall: `tools.allow` hides every profile tool — use `tools.alsoAllow`

Plugin tools are **not** part of the `coding` profile, so an installed plugin tool is registered but
invisible to the model. The obvious fix is wrong:

```bash
# ❌ REPLACES the profile's base allowlist — the agent then loses exec/read/web as well
openclaw config set tools.allow '["visual_inspect"]'
# ✅ additive: keeps the profile and adds the plugin tool
openclaw config set tools.alsoAllow '["visual_inspect"]'
```

(`allow` and `alsoAllow` are mutually exclusive in the same scope.) We hit this while building the
plugin: the agent surface collapsed to three tools and the plugin *looked* unregistered.

Verify either way:

```bash
openclaw gateway call tools.effective --params '{"sessionKey":"main","agentId":"main"}' --json | grep -i visual_inspect
openclaw agent --session-key agent:main:ovp-check -m "call visual_inspect with mode=doctor"
```

## Using the tool

| mode | what you get |
|---|---|
| `state` (default) | the full compact Visual State for screen / window / file |
| `headline` | ~400-char triage line — cheap "do I need more?" check |
| `grep` | substring search over the cached state, **plus the match's neighbours** (a label alone is rarely the answer) |
| `region` | the contents of one `x,y,w,h` rectangle |
| `json` | raw Visual State: bboxes, classes, timings, cache state |
| `doctor` | engine + permission preflight |

## What it reads — and what it does not

**Reads:** OCR text with pixel bboxes and confidence (CJK confidence runs 0.30–0.50 — low, and *not* a
failure signal); Accessibility roles, titles, values and actions (exact on native and Chromium apps);
window ids, owners, titles, z-order and the frontmost app; heuristic layout regions, marked as
heuristic and never labelled with semantics.

**Does not:**

- **macOS only.** Apple Vision, `CGWindowList` and the Accessibility API are the value; there is no
  portable equivalent worth faking.
- **No photo / chart / diagram semantics** — that needs a vision model, which is what `ESCALATE` is for.
- **No object or icon detection.** Icon-only toolbars stay unreadable; use tooltips or templates.
- **Structured dialog detection in screenshots is unsolved**, and documented as such:
  `VNDetectRectanglesRequest` saturates with small text-line rectangles and never surfaces a modal box,
  and the "dimmed backdrop + bright panel" heuristic fails because **macOS alerts do not dim the
  background** (measured border-ring median luminance 240). Keyword classification plus the 

... (truncated)
tools

Comments

Sign in to leave a comment

Loading comments...