[Bug]: snapshotText assigns no ref to listener-bearing elements without an interactive role
Affected component
ego-browser runtime or CLI
What happened?
snapshotText() gives every element an entry in the tree, but only assigns a [ref=N] handle to elements that are semantically interactive. An element that carries a click listener without an interactive role or tag appears as plain text, so there is no ref to click.
On ego lite 0.4.7.4 (Chromium 150.0.7871.101, Node v24.18.0), macOS 15.7.9 arm64, with this fixture:
<button id="b1">plain button</button>
<ul>
<li id="li1">Seoul City Hall (A-1001)</li>
<li id="li2">Seoul Metropolitan Council (A-1002)</li>
</ul>
<ul>
<li><button id="lb1">button inside item</button></li>
</ul>
<div id="clickable" role="button" tabindex="0">div with role=button</div>
<script>
window.__log = [];
for (const id of ['b1','li1','li2','lb1','clickable']) {
document.getElementById(id).addEventListener('click', e => window.__log.push({ id, isTrusted: e.isTrusted }));
}
</script>snapshotText() returns:
root
heading
text "ref probe"
button [ref=6, loc=unstable]
text "plain button"
unordered_list
list_item
text "Seoul City Hall (A-1001)"
list_item
text "Seoul Metropolitan Council (A-1002)"
unordered_list
list_item
button [ref=15, loc=unstable]
text "button inside item"
button [ref=17, loc=unstable]
text "div with role=button"The <li> elements are present in the tree, but carry no ref. They are nonetheless real click targets:
await click('xpath=//li[@id="li1"]')
await js('JSON.stringify(window.__log)')
// [{"id":"li1","isTrusted":true}]A trusted click on the li fires its listener normally.
Why this matters in practice
From an accessibility standpoint this is arguably correct: a bare <li> with a click handler is not an interactive node. But agents meet this markup constantly. In one benchmark on a large shopping site the cart's delete control was a plain div with an onClick and no role.
When the ref path is unavailable, the agent falls back to CSS/XPath selectors or raw coordinates — which is exactly where #350 applies, since element-targeted mouse helpers can silently miss or hit the wrong element. So the observation gap here feeds directly into the actionability gap there.
What would help
Some way to reach listener-bearing, non-semantic elements through the same safe path as everything else. For example: an opt-in mode that surfaces elements with registered click/pointer listeners, or a documented query that returns refs for them.
snapshotText() does take a scope option (full_page / only_within_viewport) per the shipped skill docs. I have not verified whether any existing option changes ref assignment for these elements — if one already does, documenting it would resolve this.
Version
ego lite 0.4.7.4 — Chromium 150.0.7871.101, Node v24.18.0
Environment
macOS 15.7.9 (24G830), arm64; agent-driven ego-browser nodejs heredocs
Source: citrolabs/ego-lite