Understanding the Element Resolution Pipeline in Ego-Lite

Ego-Lite's element resolution pipeline converts high-level selectors, references, and locators into concrete browser coordinates or object IDs through a multi-stage process involving parsing, snapshot mapping, and Chrome DevTools Protocol (CDP) command execution.

The element resolution pipeline is the core mechanism in citrolabs/ego-lite that bridges high-level user interactions with low-level browser internals. Located primarily in package/ego-browser/src/element-resolver.ts, this pipeline translates CSS selectors, XPath expressions, accessibility role locators, and numeric references into actionable targets that the browser runtime can interact with via CDP.

How the Element Resolution Pipeline Works

The pipeline operates through five distinct stages, each handling specific aspects of target identification and validation.

Stage 1: Input Parsing and Locator Classification

The resolution process begins by analyzing the raw input string to determine its type. The parseRef function checks if the argument follows the reference syntax (e.g., @23), while parseLocator handles standard selector strings.

For non-ref inputs, parseLocator analyzes the string and returns a locator object containing:

  • kind: The selector type (css, xpath, role, text, label, query, etc.)
  • Indexing parameters: Optional nth or last modifiers for disambiguation

This parsing logic is implemented at the start of parseLocator in element-resolver.ts (lines 14-97).

Stage 2: Reference Resolution via Snapshot Mapping

When the input is a numeric reference (e.g., @42), the pipeline queries the ref map—a data structure built from the most recent DOM snapshot that maps reference IDs to backend node IDs.

The resolution flow for references:

  1. Look up the ref ID using refMap.get(refId)
  2. If a backendNodeId exists, execute DOM.getBoxModel (for coordinates) or DOM.resolveNode (for object IDs)
  3. If the node is stale or lacks a box model, fall back to role/name lookup via the Accessibility tree

This logic is handled in resolveElementCenter (lines 70-103) and resolveElementObjectId (lines 56-86).

Stage 3: Locator Strategy Implementation

For direct selectors (non-ref inputs), the pipeline branches based on the locator's kind:

Role-based locators leverage the Accessibility tree via Accessibility.getFullAXTree, using helper functions like findBackendNodeIdByRoleName and findUniqueBackendNodeIdByRoleName to locate the matching backend node.

CSS, XPath, text, and query locators generate optimized JavaScript snippets through buildLocatorCenterJs, buildLocatorFindJs, or buildLocatorCountJs. These snippets execute inside the target page via Runtime.evaluate, returning element coordinates or handles without requiring full DOM serialization.

Locator resolution functions reside in resolveLocatorCenter (lines 50-75) and resolveLocatorObjectId (lines 33-55).

Stage 4: Box Model Extraction and Coordinate Calculation

Once a backend node ID is identified, the pipeline retrieves the element's geometry using DOM.getBoxModel, which returns the eight coordinates defining the element's border box. The boxModelCenter function (lines 53-68) calculates the precise center point from these coordinates.

If the box model is unavailable (element not yet rendered or detached), the function throws a transient error, signaling to calling helpers like waitForSelector that a retry attempt should be made.

Stage 5: Error Handling and Retry Classification

All resolution paths wrap failures in ElementResolutionError, which includes a kind property distinguishing between:

  • transient: Retryable errors (element not rendered, timing issues)
  • permanent: Non-retryable errors (selector matched multiple elements, invalid syntax)

Helpers interpret these classifications to determine polling behavior. Error construction utilities include selectorResolutionError (lines 52-61) and matchCountKind (lines 46-50).

Core Implementation Files

The element resolution pipeline spans several specialized modules:

  • package/ego-browser/src/element-resolver.ts: Core implementation containing parsing logic, resolution strategies, and error handling.
  • ref-map.ts: Constructs the reference-to-backendNode mapping from DOM snapshots.
  • ref-state.ts: Maintains mutable snapshot state powering the reference map.
  • cdp-eval.ts: Abstraction layer for CDP commands (Runtime.evaluate, DOM.getBoxModel, DOM.resolveNode).
  • locator-query.ts: Generates DOM query expressions for CSS and text-based locators.
  • helpers.ts: Public API wrappers (click, hover, waitForSelector) that invoke the resolver.

Practical Code Examples

Resolving Center Coordinates from CSS Selectors

const {x, y, sessionId} = await resolveElementCenter(
  cdp,                    // CDP connection instance
  mainSessionId,         // Current session identifier
  refMap,                // Snapshot reference map
  'css:#submit-button',  // CSS selector string
);
// x and y represent the clickable center of the element

Resolving Object IDs from Numeric References

const {objectId, sessionId} = await resolveElementObjectId(
  cdp,
  mainSessionId,
  refMap,
  '@42',  // Numeric reference from previous snapshot
);
// objectId is suitable for CDP methods like DOM.focus or Runtime.callFunctionOn

Using Role-Based Locators with Indexing

const {x, y} = await resolveElementCenter(
  cdp,
  sessionId,
  refMap,
  'role:button[name="Next"]',  // Accessibility role locator
);
// Throws permanent ElementResolutionError if multiple matches exist;
// use nth indexing (e.g., nth=0) to disambiguate

Summary

  • The element resolution pipeline in package/ego-browser/src/element-resolver.ts translates high-level selectors into browser-actionable targets.
  • The pipeline supports multiple input types: numeric refs (@23), CSS selectors, XPath, text queries, and accessibility role locators.
  • Two return modes are available: coordinate centers ({x, y}) for pointer interactions and object IDs for direct CDP manipulation.
  • Error classification (transient vs permanent) enables intelligent retry logic in helper functions.
  • Snapshot-based ref mapping provides stable identifiers across DOM mutations, with automatic fallback to locator resolution when references become stale.

Frequently Asked Questions

What is the element resolution pipeline in ego-lite?

The element resolution pipeline is the architectural component that converts human-readable selectors and numeric references into concrete browser coordinates or object identifiers. According to the citrolabs/ego-lite source code, this pipeline handles parsing, snapshot-based reference resolution, and CDP command generation to locate elements reliably across iframes and dynamic DOM states.

How does ego-lite handle stale element references?

When a numeric reference (e.g., @42) resolves to a node lacking a valid box model or backend node ID, the pipeline falls back to role or name-based lookup via the Accessibility tree. This fallback mechanism, implemented in resolveElementCenter and resolveElementObjectId, ensures that temporary DOM changes do not break automation sequences while maintaining reference stability where possible.

What types of locators does the element resolver support?

The resolver supports role locators (accessibility tree queries), CSS selectors, XPath expressions, text content matching, label associations, and query strings. Each type triggers a specific resolution strategy: role locators use Accessibility.getFullAXTree, while CSS and text locators generate JavaScript evaluation snippets via Runtime.evaluate.

How does ego-lite classify element resolution errors?

The pipeline throws ElementResolutionError instances with a kind property set to either transient or permanent. Transient errors indicate temporary conditions like elements not yet rendered, triggering retry logic in helpers like waitForSelector. Permanent errors indicate fundamental issues like ambiguous selectors or invalid syntax, causing immediate failure without retry attempts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →