Commit Graph
8989 Commits
Author SHA1 Message Date
Pierre Tachoire 6729cf389b apply a stronger rate limit on cgit websites 2026-08-28 18:04:20 +02:00
Pierre Tachoire ddb1011732 http: adjust rate limit depending the time to first byte
Compute the time to first byte received to adjust host's rate limit.
The more the server takes time to send the first byte, the more we rate
limit it.

Note: only requests send after a response is received are impacted.
Requests already queued stay untouched.
2026-08-28 14:45:04 +02:00
Pierre Tachoire 6c9d8a285d network: per-host pressure in the navigation rate limiter
The interval between two top-level navigations to the same host now
grows with the number of navigations sent to it: one extra base interval
every burst*10 navigations, capped at 60x. Pressure cools down by one
unit for every 10 base intervals the host is left alone.
--http-nav-delay is now a minimum, not a fixed spacing.
2026-08-28 14:43:32 +02:00
Karl Seguin 054b0b40ed Merge pull request #3322 from lightpanda-io/fix/agent-js-history
agent: fix agent history in JS mode
2026-08-28 20:35:53 +08:00
Adrià Arrufat 9126fd9540 Merge pull request #3324 from lightpanda-io/cdp-method-not-found
cdp: answer unknown methods with -32601 like Chrome
2026-08-28 13:36:02 +02:00
Adrià Arrufat 9a64780c10 cdp: answer unknown methods with -32601 like Chrome
Unknown domains and methods returned our private -31998 with the Zig
error name. Drivers feature-detect on Chrome's -32601 "'X' wasn't
found", so return that; other dispatch errors keep -31998.
2026-08-28 09:33:32 +02:00
Adrià Arrufat 0d1e3c0c8b agent: fix agent history in JS mode 2026-08-28 09:07:16 +02:00
Karl Seguin fa1b9ab03d Merge pull request #3316 from staylor/fix/mutation-observer-runaway
stability: disconnect runaway mutation observers
2026-08-28 10:06:45 +08:00
Karl Seguin 0f91a0f643 Simplify mutation limit tracking
hit_limit_this_session is more explicit as a local.
2026-08-28 09:44:30 +08:00
Karl Seguin 3ee9bb0ab0 Merge pull request #3315 from staylor/fix/css-property-value-release
css: reuse storage for updated property values
2026-08-28 09:04:44 +08:00
Karl Seguin b584b5f376 Merge pull request #3283 from staylor/fix/immediate-meta-refresh-navigation
Handle immediate meta refresh navigation
2026-08-28 07:44:42 +08:00
Karl Seguin 2670af68ad Merge pull request #3308 from lightpanda-io/screenshot-improvements
tweak: improve screenshot code
2026-08-28 07:44:25 +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 4add53a924 zig fmt 2026-08-28 07:05:03 +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
Karl Seguin 1ec1fb4ac4 log on ignored meta refresh 2026-08-28 07:01:35 +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
Scott Taylor 105ba20b05 stability: disconnect runaway mutation observers
Track delivery sessions that repeatedly hit the recursion cap without draining. Disconnect the frame's observers after 100 such sessions so timer-rearmed mutation cascades cannot consume memory indefinitely.

Assisted-By: devx/1a7af220-8162-4dc8-b16c-088baab97324
2026-08-27 16:11:50 -04:00
Scott Taylor 526f4a4718 css: reuse storage for updated property values
Allocate mutable CSS property values from the factory slab instead of the frame-lifetime arena. Replacing or removing a property now returns its owned value storage for reuse without attempting to free interned strings.

Assisted-By: devx/1a7af220-8162-4dc8-b16c-088baab97324
2026-08-27 16:11:50 -04:00
Adrià Arrufat ce6f87f3c2 Merge pull request #3309 from lightpanda-io/mcp-http-single-buffer
mcp: release a large HTTP response buffer after the reply
2026-08-27 21:45:21 +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 f5e89abd03 Merge branch 'screenshot-tool' of https://github.com/lightpanda-io/browser into screenshot-tool 2026-08-27 21:11:03 +02:00
Adrià Arrufat a1c4a67c08 Merge pull request #3307 from lightpanda-io/agent-terminal-tests
agent: run the Terminal tests
2026-08-27 16:59:26 +02:00
Adrià Arrufat 51dc7872b1 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 16:49:18 +02:00
Adrià Arrufat 7318fbfcec agent: use zenai's hasImage
zenai exports it now; drop the local copy and re-pin.
2026-08-27 16:49:18 +02:00
Adrià Arrufat 051a70cfdd 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 16:49:18 +02:00
Adrià Arrufat d34d3a0f28 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 16:49:18 +02:00
Adrià Arrufat cadf8f9f64 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 16:49:18 +02:00
Adrià Arrufat 30669acb0e 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 16:49:18 +02:00
Adrià Arrufat 4a516027d3 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 16:49:18 +02:00
Adrià Arrufat 9239e1eadd 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 16:49:18 +02:00
Adrià Arrufat 8e122fcb1e Merge pull request #3302 from lightpanda-io/readme-agent-providers
readme: list all agent providers and gateway examples
2026-08-27 16:48:10 +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
Karl Seguin a0e21b9aeb tweak: improve screenshot code
Remove singleton and give each Browser a screenshot.Renderer.

Also, add tests to make sure the Rust and Zig structures/constants match (this
has proven useful with zig-v8-fork).
2026-08-27 19:22:04 +08:00
Karl Seguin 585ed9b93c agent regression explictly load workers and iframes 2026-08-27 18:32:36 +08: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