A flag given without its value, or an extra positional, failed with
only `FATAL exit err=MissingArgument`, not naming the flag. The parser
now logs which flag is missing its value or which argument is extra,
with a hint pointing at the command's help, and a missing fetch URL is
caught while parsing, next to run's missing script. Since each of these
is logged where it's found, main exits without the generic `exit` line.
`-h` works wherever `--help` does, instead of being taken as a URL.
The --obey-robots tip is for a person at a terminal, so it's skipped
when stderr isn't one; scripts capturing stderr no longer get it on
every run.
The easy-handle pool is shared by every client in the process. When other
sessions hold all of it, a new navigation's request sits in pending_queue,
but Runner resolved a frame still in `.pre` as soon as in-flight activity
was zero, which ignores pending transfers. goto then reported success on
an empty document.
Wait on `activity.idle()` instead, and make a goto that times out before
any response arrives a NavigationTimeout error rather than a soft timeout.
Fixes#3636
tree, interactiveElements, structuredData, detectForms and scroll were
one or two sentences, and evaluate's script parameter had none. MCP
clients that drop the server instructions only see these, so state what
each returns (fields, shapes, empty cases), when to reach for it over
its neighbours, and scroll's absolute-position semantics.
Picks up effort=none sending thinking disabled to Anthropic (zenai#19)
and the reasoning max_tokens floor on every provider (zenai#18).
ErrorDetail now carries the parsed error message instead of the raw
body.
- Synthesis prompt: drop the one-word/one-phrase answer clamp (a
benchmark format applied to every budget-exhausted REPL turn) and the
no-more-tools line the request already enforces; keep the
report-don't-confabulate rule.
- Driver guidance: drop the HN/Reddit source heuristic, which also
reached every MCP client.
- Save prompt: state the comment rule once instead of repeating it in
the output-format line.
- goto: describe when it returns, what it returns, and when a url-taking
read is the better call.
The list marker is written by writeNode, so writeTree and walkQuery share
one loop shape. IgnoreCache lives on its own pooled arena for the call
instead of frame.call_arena, which only resets on JS calls. The scan no
longer caches its root, which is never asked about again.
A div/span with no role or label is ignored unless a non-ignored node
sits under it through other generic containers. The tree writer asks
that of every node, so each generic container rescanned the chain below
it: O(depth²). A 20k-deep div chain took 5.5s in getFullAXTree.
Answers are now cached per walk. A scan marks every generic container it
enters as ignored, then flips the ones on the path to the exposed node
it stops at. Later questions hit the cache, so the walk is linear:
20k-deep chain 5.5s -> 12.5ms, 2k-deep 46ms -> 1.3ms (ReleaseFast).
Output is byte-identical.
Follow-up to the SemanticTree change: the CDP accessibility writer still
recursed once per DOM level in writeNodeChildren (getFullAXTree /
getPartialAXTree), walkQuery (queryAXTree) and isIgnore (chains of
generic div/span containers). In a debug build, a 100k-deep chain
overflows the 8MB main-thread stack in the tree writer and in isIgnore.
The tree and query walks now share an allocation-free pre-order Walker
that counts aria-hidden ancestors instead of threading the flag through
the recursion. isIgnore splits into a shallow per-node verdict
(ignoreSelf) and a TreeWalker pass over generic containers that skips
ignored subtrees.
Output is byte-identical to the recursive version.
A box whose content is only text had no content extent, so its scroll
offset never clamped. With an explicit width, direct text children now
wrap at it and add their lines to the content height.
An element sized by a stylesheet rule had no explicit size, so its scroll
offset never clamped. The geometry group now tracks width and height, and
getElementAxis reads them from the cascade, which already folds in the
inline style. html and body read only their inline size, without
materializing the style object. Removes the now unused
CSS.parseDimensionViewport.
rebuildIfDirty walks group_fields like the other per-group code, so a new
group needs no change there. Capacities moves out of Group since it
doesn't depend on the spec. Also updates the ownProps and compute call
sites to the (el, frame) order.
The slab allocator was meant to enable efficient re-use of freed memory. In
reality, the allocations made to it are rarely freed:
```
│ site │ allocs │ freed │
│ reddit /r/programming │ 50,462 │ 4.6% │
│ wikipedia article │ 8,557 │ 3.4% │
│ youtube │ 4,913 │ 9.5% │
│ bbc news │ 4,734 │ 1.1% │
│ github repo │ 4,117 │ 3.3% │
│ react.dev │ 3,640 │ 0.3% │
│ HN │ 1,313 │ 0.1% │
```
And, just because allocations are freed doesn't meant that memory gets to be
re-used. reddit has HTMLCollection 1120 total HTMLCollection allocation, with
a peak inflight of 862, so the majority of frees weren't useful.
I think the ArenaPool came after the slab, and it became the preferred (but not
exclusive) mechanism for managing eagerly freed memory.
This replaces the slab allocator with a simpler recycler. Unlike the slab, it
doesn't degrade in performance as the # of allocations grow (310ns/op vs 17ns/op
at 500K items) and it doesn't leak memory (the parent allocator *is* the page's
arena, so the Slab's bitset growth will leak unless it can grow in place).
Each group owns its rule buckets, its memo and its cascade priorities, so a
rule only joins the groups it declares something in. Visibility keeps
display, visibility, opacity and pointer-events; overflow and
overscroll-behavior move to a geometry group. A visibility probe no longer
matches overflow-only rules, and its memo entry shrinks to 6 bits.
Response headers get lower cased once, upfront. Any consumer of a transfer's /
response's headers is now `mem.eql` rather than `ascii.eqlIgnoreCase`. This
fixes 1 or 2 WPT cases (e.g. XHR's `getAllResponseHeaders`), it also mergers
values in some cases (which is generally correct) - we need a follow up PR
to correctly merge in all cases.
Preloaded modules would discard errors in the hope that re-fetching the script
when it was needed, might work. This reverses that decision and stores the
error state. When a preload fails, that failure sticks.
This does mean that a transient failure doesn't get a second chance.
But it also means we don't log the failure multiple times (I noticed this when
using adblocking and seeing duplicate failures for the same endpoint). Since we
don't keep trying, it cuts down the CDP flow too.