Parse the HTTP `Link:` response header (RFC 8288) and the robots.txt
`Content-Signal` directive, surfacing both to the driving agent.
- structured_data: extract the registered Link relations (service-doc,
service-desc, api) from the frame's retained response headers into a
new `linkHeaders` field, flowing out via the structuredData MCP tool
and LP.getStructuredData. Emitted only when present.
- Robots: capture Content-Signal preferences on a new content_signals
field, read back via RobotStore.getContentSignals and the new
LP.getContentSignal CDP command. Advisory only - never reaches
isAllowed.
Refs #2680
Rewrite:
- Section headings now describe tasks ("Log in to Hacker News" vs "Driving the browser by hand").
- Section 3 leads with the five login commands; selector-hunting workflow moved to optional subsection at the end.
- Trimmed prereqs (dropped fish variant, $-escape edge cases, long LP_ prefix explanation).
Every working command preserved. Load-bearing callouts in section 3 kept (/waitForSelector doing two jobs, CSS-only selectors).
The agent.md rewrite dropped documentation for several features that still
exist in the code. Restore them and clean up the markdown:
- Re-add /clear and /reset to the meta-commands table
- Re-add waitForState to the browser-tools list and the /save
state-mutating command list
- Re-add --system-prompt and --verbosity (with the --task stderr-pipe
escalation) to the providers section
- Strip trailing whitespace on blank lines; add blank lines before
headings and between lists and following paragraphs
async_scripts is used to block the "load" event, and preload scripts (a)
shouldn't block this and (b) might never be used. The `preloaded_scripts` map
itself can be cleaned up on shutdown/reset.
- **New opening.** Direct framing: "lets you drive a headless browser by talking to it."
- **New "How to think about it" section.** Explains the three-layer stack and the one-sentence pitch ("pay for the LLM once, then replay deterministically").
- **New "The REPL is where you build scripts" section.** Positions the REPL as a sandbox with a full sandbox-to-saved-script example.
- **New tips section.** Practical guidance for prompts that produce useful saved scripts (be specific about extracted data, name the page, check the file).
- **Updated Quick start.**
Swap `_previous_states: Map(*Element, Bool)` to `_tracked: Set(*Element)` since
we only care about membership (the value was previous always bool). This better
captures the intent.
`checkIntersection` can be called a lot...on every domChanged. And it can call
a known expensive API: `getBoundingClientRect`.
This commit reworks the code to be able to exit from checkIntersection early.
`_previous_states` is now seeded with the target so that, once reported, the
lack of entry can be used to quickly exit.
When profiling airbnb, `checkIntersection` was reported 841 times. With this
change, it was reported twice.
Implement <link rel=preload as=script href=...>. The implementation is similar
to preloadImport / waitForImport. Consider a website that does something like:
```
// pseudo html
<head>
<link preload script1.js>
<link preload script2.js>
<link preload script3.js>
<link preload script4.js>
<script script1.js></script>
<script script2.js></script>
<script script3.js></script>
<script script4.js></script>
```
Then, without preloading, we hit <script script1.js></script> and block while we
load it + execute it. Repeat for 2, 3, 4 ...
With preloading, by the time we block on <script script1.js></script> all the
scripts are already being downloaded in the background.
I opted to remove the script on first use. If a script happens to be used twice
(we have seen this happen for imports, but I guess it's more rare on blocking
scripts), then it'll get re-downloaded the 2nd time, just like before (and just
like before, the http cache is a better mechanism to rely on here).
airbnb preloads 41 scripts.
Extracts chat history and arena management into `Conversation.zig`,
the startup banner into `welcome.zig`, and model/config resolution
helpers into `settings.zig` to simplify `Agent.zig`.
The TestHTTPServer leaks a thread for every connection that shuts down. This
commit correctly releases it. It also closes a leaking CDP socket.
Full test went from 20s -> 10s for me
Rather than relying on the frame_arena, use a distinct arena for FormData. This
generally results in tighter memory usage, but more importantly it ensures that
if FormData outlives the frame, we don't get a UAF. This can happen if the
FormData is refernced in v8 and finalized late (e.g. after the frame would
appear to still be needed).
Also, in Frame.submitForm use the explicit acquireRef and releaseRef. This
FormData can [in theory] be passed to JS, via the `formdata` event that we fire.
DragEvent extends MouseEvent (new drag_event arm in the MouseEvent type union)
and exposes a nullable dataTransfer, so JS can build a DataTransfer and dispatch
a drop/dragover event whose .dataTransfer.files is readable. Also resolves the
InputEvent dataTransfer TODO with a real field + accessor.
Both events hold a refcount on the DataTransfer for their lifetime and release
it in deinit (via the leaf acquireRef/releaseRef/deinit trio, like MessageEvent
holds a Blob), so the store's arena can't be freed out from under an event whose
JS wrapper is collected first.
Refs #2043
Implements the constructible DataTransfer interface and its item views over a
single drag-data-store: string- and file-kind items, with .files (FileList) and
.types derived from the file items so items.add(file) is visible immediately.
File refs are acquired on add and released on remove/clear; the backing FileList
is frame-tracked and the DataTransfer arena is refcounted so the GC finalizer
reclaims it, leaving no leaks.
Refs #2043
Replace the monochrome braille logo with a truecolor ANSI-colored
version. Update logo dimensions and row counting logic, and simplify
the welcome banner printing to stream logo lines directly.
Statically assert that the banner fits within 80 columns at compile
time. This removes the need to dynamically measure terminal width
and conditionally hide the logo.
Updates the isocline dependency to use its native hints for Ctrl-D
and Esc. Removes the custom status bar rendering logic, and prints
the active model and effort level at REPL startup instead.
Prints a Braille-art Lightpanda logo alongside the welcome text and
command hints if the terminal is wide enough. Otherwise, falls back
to a text-only layout.
Also exposes `Terminal.displayWidth` to assist with layout calculations.