Commit Graph
7284 Commits
Author SHA1 Message Date
Adrià Arrufat be4feb3488 agent: draw a REPL /screenshot in terminals that speak kitty graphics
A /screenshot without a path had nowhere to go in the REPL: the image
went to the model when one was attached and was refused otherwise.
Probe the terminal once (graphics query followed by Device Attributes,
which every terminal answers and so bounds the reply) and, when it
speaks the kitty protocol, transmit the PNG it already produces, scaled
by the terminal into the window's width and a screenful of rows. The
tool is only asked for an image when the terminal or a model can take
it, so an unsupported terminal keeps the tool's own refusal; when the
model alone took it, the outcome line says so. The PNG is rendered once
and shared between the terminal and the conversation.

The raw-mode and window-size helpers the picker and spinner carried
move to tty.zig so the probe doesn't grow a third copy.
2026-08-28 07:35:28 +08:00
Adrià Arrufat 97f91b3134 Merge pull request #3301 from lightpanda-io/screenshot-tool
tools: add a screenshot tool
2026-08-28 07:35:25 +08:00
Karl Seguin 72d668f973 Merge pull request #3319 from lightpanda-io/no-intfromfloat
zig: replace the deprecated @intFromFloat with @trunc
2026-08-28 07:11:25 +08:00
Karl Seguin ddcac4ee60 Merge pull request #3305 from lightpanda-io/disable-iframes-and-workers
breaking: disable worker and iframe loading by default
2026-08-28 07:04:06 +08:00
Karl Seguin b943870f2c Merge pull request #3306 from lightpanda-io/transfer-submit-double-free
http: fix possible double-free on submit error
2026-08-28 07:03:32 +08:00
Karl Seguin 2840e5c1d8 Merge pull request #3299 from lightpanda-io/search-params-url-sync
webapi: searchParams keeps a linked URL in sync
2026-08-28 07:03:04 +08:00
Karl Seguin aea04ce85c Merge pull request #3298 from lightpanda-io/wpt-text-editing-events
webapi: edit key support in input/textarea
2026-08-28 07:02:48 +08:00
Karl Seguin 9abfd8eca0 Merge pull request #3297 from lightpanda-io/fix-with-base-attribute
fix: --with-base now uses proper attribute
2026-08-28 07:02:30 +08:00
Adrià Arrufat da8aa307a6 zig: replace the deprecated @intFromFloat with @trunc
Zig 0.16 forwards the result type through the unary float builtins, so
@trunc produces the integer directly and @intFromFloat is deprecated as
redundant with it. Every site was a truncating cast, or already held an
integral value, so the behaviour is unchanged.
2026-08-27 22:39:30 +02:00
Adrià Arrufat e6165d23bb mcp: release a large HTTP response buffer after the reply
The transport already drops its staging buffer past 256 KB, but the
connection buffer that receives the copy lives for the whole keep-alive
connection and was only ever cleared, so one screenshot pinned its
capacity per open connection. Apply the same threshold there.

Claude-Session: https://claude.ai/code/session_01S9t1TX3vTKunaXdnatbcjB
2026-08-27 21:28:32 +02:00
Adrià Arrufat d10029fb19 Merge branch 'main' into screenshot-tool 2026-08-27 21:13:36 +02:00
Adrià Arrufat a8340a10b7 tools: report a fixed strip's own height for a saved screenshot
The renderer returns the content height; a viewport shot of a short
page said 1920x184 for a 1920x1080 file.
2026-08-27 15:08:24 +02:00
Adrià Arrufat e70b316aea agent: use zenai's hasImage
zenai exports it now; drop the local copy and re-pin.
2026-08-27 15:03:19 +02:00
Adrià Arrufat 45744407af screenshot: fit() owns the inline sizing; prune() owns image expiry
The renderer knows that layout reflows to the width and that height 0
means the whole content, so the bounding rule moves next to measure()
and only measures when the strip isn't already fixed: the default
viewport shot no longer pays a second layout pass. The limits are
constants spliced into the tool description, not a caller-tunable that
nothing tuned. expireImages runs inside prune so there is one end-of-
turn hook, and re-homes a stripped result instead of casting away const.
2026-08-27 15:03:19 +02:00
Adrià Arrufat c693979468 agent: bound what a screenshot costs the model
An inline screenshot is re-sent on every request for as long as it sits
in history, and a full-page render could reach 1920x16384. Inline images
are now rendered at most 1280 wide and 4096 tall (measured after the
reflow, so short pages aren't padded), files keep full size; the
conversation keeps only the newest two images, older tool results keep
their text with a note; the MCP transport releases its buffer after a
large response.
2026-08-27 15:03:19 +02:00
Adrià Arrufat cff32f6c04 agent: let the model see a REPL /screenshot; fold the tool-result adapters
The slash path's result has two consumers: the terminal, which can't
show an image, and the conversation, which can. Opt in when a model is
attached and forward the image through the same adapter the model-driven
path uses, and stop mapping a failed adapter to a text-only success.
MCP's ImageContent and CallToolResult take their payload type like
TextContent does; resolveScope reuses resolveTarget; needsLocator folds
into replayRequires.
2026-08-27 15:03:19 +02:00
Adrià Arrufat e6203ff1fe agent: hand the model inline screenshots
zenai tool results now carry image parts (lightpanda-io/zenai#12, #13),
so the model-driven tool path opts into inline images: a screenshot
without `path` reaches the model as text plus the PNG, on every backend.
The slash-command path still needs `path`, its result goes to the
terminal.
2026-08-27 15:03:19 +02:00
Adrià Arrufat 193f15558c tools: declare image support at the call site, not in each consumer
Callers pass CallOpts.inline_image; execScreenshot rejects a path-less
call before navigating or rendering, which removes the per-consumer
guards and covers the model-driven tool path that had none. MCP image
content is a protocol type, the screenshot recording rule joins the
recorder's replayRequires predicate, and the viewport-to-Opts mapping,
node-scope ladder and save-path helpers are shared instead of copied.
2026-08-27 15:03:19 +02:00
Adrià Arrufat 56c6a0a7ab tools: add a screenshot tool
The PNG renderer was reachable from CDP and fetch --dump png only. The
tool renders the page or one node; with `path` it writes the file and
returns its location (agent, PandaScript, MCP), without it MCP returns
the image inline as base64 content. ToolResult carries the prepared
image so the transport streams it; the agent and script runtime reject
the inline form since their tool results are text. An inline screenshot
is not recorded, as it has no replayable form.
2026-08-27 15:03:19 +02:00
Adrià Arrufat ed99a5b79f agent: run the Terminal tests
Terminal.zig references md_term and prompt_assist from a test block, but
nothing referenced Terminal, so those 30-odd tests never ran. Adding it
to Agent.zig's discovery block surfaced one rotted test sink in
js_highlight.zig still using ArrayList.writer(), ported to
Io.Writer.Allocating.
2026-08-27 12:03:23 +02:00
Karl Seguin b137365394 http: fix possible double-free on submit error
Similar to https://github.com/lightpanda-io/browser/pull/3289.

This pattern:

```zig
errdefer freesomething()
try transfer.submit();
```

is dangerous.`sumbit()` guarantees that the errorCallback is called on any
error. So if the transfer's errorCallback also does `freesomething()` then we
end up with a double-free.

The code was generally cleaned up to be:

```zig
{
  errdefer freesomething();
  try transfer.addHeader(....);
}
transfer.submit() catch {};
```
2026-08-27 17:45:16 +08:00
Karl Seguin 04c8a4c3a6 webapi: searchParams keeps a linked URL in sync
Mutating a searchParam that was created from a url.searchParams now keeps the
URL in sync. This helps ensure the that url.search keeps its original form.
Improves a WPT url-searchparams.any.html and urlsearchparams-stringifier along
with a few other cases.
2026-08-27 17:28:01 +08:00
Karl Seguin 861ffb164b enable --log-filter in release
Log on the first iframe/worker that refuses to load due to iframe/worker loading
being disabled.
2026-08-27 17:25:42 +08:00
Karl Seguin db258bfec4 breaking: disable worker and iframe loading by default
By default, iframes and workers no longer loaded. Use `--load-resources iframe`
and `--load-resources worker` to restore the previous behavior. The disabling
makes resource loading more consistent.

To further make things more consistent, Config seems the following changes:

1. remove  `--timeout` from `serve` which does nothing but has printed a
   deprecation warning for a long time

2. added .deprecated field to CLI config flags which now logs the specified
   deprecated warning when used

3. `--log-filter-scopes` is deprecated in favor of `--log-scopes`

4. `--disable_subframes` is deprecated. Iframe loading is disabled by default,
    use `--load-resources iframe` to enable iframe loading

5. `--disable_workers` is deprecated. Worker loading is disabled by default,
    use `--load-resources worker` to enable worker loading

6. `--enable_external_stylesheets`  is deprecated. Stylesheets remain disabled
    by default. Use `--load-resources stylesheet` to enable loading external
    stylesheets

CLI log parameters now alter the logger behavior on parse. This helps minimze
the window where default log settings are in-play. It also means things like
this work:

```
./lightpanda --log-level fatal --disable_subframes --log-level warn
```

More seriously, there's now an optional `beforeParse` fired once the mode is
known. This is used by mcp to set the default log level to logfmt. Previously
this was done much later and could easily result in a mix of pretty and logfmt
logs.
2026-08-27 17:25:42 +08:00
Karl Seguin 1b59bc8d3b fix the wpt build 2026-08-27 15:23:45 +08:00
Karl Seguin 6931db8aca webapi: edit key support in input/textarea
A trusted backspace/delete or printable character now:
1 - fires beforeinput and textInput (which can cancel the edit)
2 - change the input's value

Input and TextArea share enough in common and this change has enough selection
logic that a new text_entry.zig helper is added to implement the shared logic.
2026-08-27 11:17:48 +08:00
Karl Seguin abf5bdecb6 mem: Use new clearResponse() to eagerly free XHR JSON response
And, add blob tests
2026-08-27 09:42:02 +08:00
Muki Kiboigo 3d42ffa0f8 properly ref count blob in XHR response 2026-08-27 09:20:34 +08:00
Muki Kiboigo adb4551362 support Blob as XHR responseType 2026-08-27 09:20:34 +08:00
Karl Seguin 4f63f6f204 fix: --with-base now uses proper attribute
Improve dump's void element list
2026-08-27 08:32:53 +08:00
Karl Seguin b5656eed82 Merge pull request #3292 from lightpanda-io/fetch-markdown-opts
fetch: add --selector and --max-bytes, honor --strip-mode for markdown
2026-08-27 08:28:20 +08:00
muki 57b86112b3 Merge pull request #3240 from lightpanda-io/xhr-async-flag
XHR Sync Request
2026-08-26 17:11:48 -07:00
Karl Seguin 05627c5368 handle error in json+png error
improve log error message when 1 of multiple URLs fails to fetch
2026-08-27 08:11:15 +08:00
Karl Seguin b5c25db3fc chore: silence test log 2026-08-27 07:42:23 +08:00
Karl Seguin fce38a1aa8 Merge pull request #3289 from lightpanda-io/fix-double-releases
fix: Fix double-release and other (edge-case) memory issues
2026-08-27 07:34:45 +08:00
Karl Seguin 00a2a78303 Merge pull request #3288 from lightpanda-io/WorkerNavigator
WebAPI: Add WorkerNavigator
2026-08-27 07:34:31 +08:00
Karl Seguin c70557ad08 Merge pull request #3287 from lightpanda-io/wpt-mouse-boundary-events
webapi: improve hover events
2026-08-27 07:32:34 +08:00
Karl Seguin e0f6eb5e04 Merge pull request #3286 from lightpanda-io/usp-iterable-init
webapi: URLSerachParams @@iterator init
2026-08-27 07:32:03 +08:00
Muki Kiboigo bcd77bf462 set ready state to done on sync XHR error 2026-08-26 08:05:53 -07:00
Muki Kiboigo b20bb1f553 get rid of extra releaseSelfRef 2026-08-26 08:05:23 -07:00
Adrià Arrufat 33b8bdbb4c fetch: --dump-selector without --dump is an error, like --dump-max-bytes 2026-08-26 15:45:24 +02:00
Adrià Arrufat 56241e26b8 dump: take max_bytes in Opts
Review feedback: markdown caps its own output through Opts.max_bytes while
the html dump left it to each caller, so the html tool and fetch each
carried a copy of the LimitedWriter wrapping. root and deep now do it,
with a no-cap fast path so innerHTML/outerHTML are unaffected.
2026-08-26 15:44:48 +02:00
Adrià Arrufat 6811cc8226 fetch: per-page errors, an "error" field in --json, --fail-on-http-error
A wait failure on one URL aborted the whole multi-URL fetch with no
output, the wait_ms budget was computed once before the loop so N pages
could take N times the budget, navigation failures exited 0 with an empty
dump, and HTTP 4xx/5xx were indistinguishable from success by exit code.

Each page now has its own error slot: wait, navigation and dump failures
are recorded there instead of returned, every page is still written (the
JSON envelope carries the name under "error"), then the first failure is
returned so the process exits 1. The remaining budget is recomputed per
page. --fail-on-http-error turns a status >= 400 into exit 22, curl's
code for the same condition; the dump is written first either way.
2026-08-26 15:35:15 +02:00
Adrià Arrufat 1fb7a1b983 fetch: namespace the dump options in Config too 2026-08-26 15:34:15 +02:00
Adrià Arrufat e46964e261 markdown: reuse dump's shouldStripElement 2026-08-26 15:32:25 +02:00
Adrià ArrufatandKarl Seguin c78c1d3823 fetch: namespace dump flags
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-08-26 15:28:18 +02:00
Adrià Arrufat 5870ce1120 refactor(links): use accessible name from AXNode for link text 2026-08-26 15:16:56 +02:00
Adrià Arrufat d3bad960c3 fetch: add --selector and --max-bytes, honor --strip-mode for markdown
--strip-mode was silently ignored for --dump markdown, and fetch had no
way to scope a dump to one element or cap its size although the markdown
and html tools have both.

--selector dumps the first matching element in any dump mode, --max-bytes
caps html and markdown with the tools' truncation marker, and markdown
now honors strip ui (images; scripts, styles and hidden elements are
never rendered, so the other groups don't apply).
2026-08-26 13:23:18 +02:00
Adrià Arrufat fbb9f8c8fa links: fix test expectation, the test frame has no base URL 2026-08-26 13:14:30 +02:00
Adrià Arrufat 6dca2c8917 links: dedup by href, skip hidden, fall back to alt/aria-label/title
The links tool returned every a[href] verbatim: hidden nav entries,
one row per duplicate href, and null text for image and icon links even
though markdown already knows to use alt. Nav-heavy pages produced
thousands of rows for an agent looking for one link.

One entry per resolved href (the first, upgraded with text from a later
duplicate), hidden anchors skipped with the same check tree uses, text
falling back to aria-label, title, then a descendant img alt. The tool
gains an optional limit.
2026-08-26 13:13:08 +02:00