Skip to content

feat: Ref-Backed Locator for browser actions#1016

Merged
jackwener merged 4 commits intomainfrom
feat/ref-backed-locator
Apr 14, 2026
Merged

feat: Ref-Backed Locator for browser actions#1016
jackwener merged 4 commits intomainfrom
feat/ref-backed-locator

Conversation

@jackwener
Copy link
Copy Markdown
Owner

Summary

  • Introduces a unified target resolution pipeline (resolveTargetJs) that replaces the ad-hoc 4-strategy fallback in dom-helpers.ts
  • Adds fingerprint verification: each interactive element gets a semantic fingerprint (tag, role, text) at snapshot time, verified at action time to detect stale refs
  • Structured error diagnostics with three codes: stale_ref (element identity changed), ambiguous (CSS matched multiple), not_found (element gone) — all with actionable hints
  • Two-phase action execution in base-page.ts: Phase 1 resolves + validates target, Phase 2 executes click/type on verified element

Changes

  • New src/browser/target-resolver.tsresolveTargetJs(), clickResolvedJs(), typeResolvedJs()
  • New src/browser/target-errors.tsTargetError class with code, hint, candidates
  • Modified src/browser/dom-snapshot.ts — generates __opencli_ref_identity fingerprint map during snapshot
  • Modified src/browser/base-page.tsclick() and typeText() use two-phase resolve-then-act pattern
  • New test files for target-resolver and target-errors

Design context

Discussed in thread with @codex-coder. Key insight: OpenCLI doesn't need a full Locator DSL (agent loop gets fresh snapshot each turn). The real gap is verification (detecting stale/ambiguous refs) and diagnostics (structured errors with actionable hints).

Test plan

  • All 199 test files pass (1500 tests)
  • Manual: opencli browser state → click by ref → verify fingerprint check works
  • Manual: click stale ref after page navigation → verify stale_ref error with hint
  • Manual: CSS selector matching multiple elements → verify ambiguous error with candidates list

Introduces a unified target resolution system with fingerprint
verification and structured error diagnostics.

Snapshot phase:
- Each interactive element now gets a fingerprint (tag, role, text,
  ariaLabel, id, testId) stored in window.__opencli_ref_identity
- Zero overhead: metadata is already available during DOM walk

Resolution phase (new target-resolver.ts):
- Numeric input → ref path with fingerprint verification
- CSS-like input → querySelectorAll with uniqueness check
- No more silent first-match: ambiguous selectors are rejected

Error model (new target-errors.ts):
- stale_ref: element identity changed since snapshot
- ambiguous: CSS selector matched multiple elements (with candidates)
- not_found: element not in DOM or invalid input
- All errors include actionable hints for AI agents

base-page.ts:
- click() and typeText() now use two-phase resolve-then-act
- Existing CDP fallback for click preserved
scrollTo now uses the same two-phase resolve-then-act pattern as
click and typeText, getting fingerprint verification and structured
error diagnostics (stale_ref/ambiguous/not_found) for free.
…getError in CLI

1. Fingerprint verification now uses the full identity vector (tag, id,
   testId, ariaLabel, role, text) instead of just tag/role/text. Strong
   identifiers (id, testId) are decisive; remaining signals use majority
   voting. Fixes false negatives where same-tag elements swapped.

2. browserAction() now renders TargetError with code, hint, and
   candidates list instead of just the message string.
- browser get text/value/attributes now resolve via resolveTargetJs
  instead of raw querySelector, getting fingerprint verification and
  structured errors for free
- browser select uses selectResolvedJs on __resolved element
- type command's autocomplete detection uses isAutocompleteResolvedJs
  on the already-resolved element
- Fix empty-string text prefix match: fp.text="Login" + text="" no
  longer falsely passes fingerprint check
@jackwener jackwener merged commit ca68f39 into main Apr 14, 2026
13 checks passed
@jackwener jackwener deleted the feat/ref-backed-locator branch April 14, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant