Feature · Perception

DOM first. Vision when DOM stops working.

Tanvrit Automator prefers DOM perception because it is cheaper, faster, and more accurate than asking a vision model what is on screen. Vision is the fallback for the small but real population of pages where the DOM is mostly canvas, mostly a single iframe, or mostly empty.

DOM perception (default)

DOMPerception asks Playwright for the page's accessibility tree. The tree carries every interactive node (buttons, links, inputs, comboboxes), its ARIA role, accessible name, computed bounds, and visibility.

We canonicalise the tree into a list of UIElement records, prune nodes that are off-screen or aria-hidden, and emit a PageSummary containing URL, title, viewport size, and the visible UIElement list. PageSummary is what the planner sees in its prompt.

data class UIElement(
  val id: Int,
  val role: String,
  val name: String,
  val value: String?,
  val bounds: Bounds,
  val ancestorChain: List<String>,
  val isInteractive: Boolean,
)

data class PageSummary(
  val url: String,
  val title: String,
  val viewport: Size,
  val elements: List<UIElement>,
)

Why DOM is preferred

When vision activates

The agent counts consecutive empty DOM perceptions. Once the count reaches DOM_EMPTY_THRESHOLD (2 by default), VisionPerception takes over for the next step.

Vision uses qwen2.5-vl via Ollama to caption the screenshot, plus Tesseract OCR to extract any visible text the vision model missed. The combined output is mapped back into PageSummary form so the planner sees the same shape regardless of which strategy produced it.

Worked example — a SPA with mostly-canvas DOM

Consider a Figma-style design tool. The page is one <canvas> element with no a11y children — the DOM tree returns an effectively empty list of interactive nodes. The agent observes:

Step 0: PERCEIVE — DOMPerception → 0 interactive nodes
Step 1: PERCEIVE — DOMPerception → 0 interactive nodes
        DOM_EMPTY_THRESHOLD reached → switch strategy
Step 2: PERCEIVE — VisionPerception
        qwen2.5-vl: "Top toolbar with File, Edit, View menus.
                     Left sidebar with shape tools. Centre canvas
                     with a blue rectangle. Right panel with
                     properties for the selected shape."
        Tesseract: ["File", "Edit", "View", "Width: 240"]
Step 3: PLAN — choose action targeting vision-derived bounds
Step 4: EXECUTE — VisionExecutor clicks at (x, y)

The planner now has a usable representation. The executor switches to VisionExecutor for actions that target pixel coordinates. The rest of the loop is unchanged.

Honest caveats

Vision is the deep fallback, not the default. We do not recommend turning DOM_EMPTY_THRESHOLD to 0 — vision is slower, less accurate, and consumes more memory. If your target app is mostly DOM, the default thresholds will keep vision dormant and the agent fast.

Tesseract requires its language pack to be installed. Garbled OCR output usually means the trained data for your language is missing. See Troubleshooting.