Commit Graph

7016 Commits

Author SHA1 Message Date
Adrià Arrufat
fb22f0e875 agent: use local arenas to avoid re-entrant corruption
Re-entrant JS callbacks (like `toJSON` or `toString`) could reset the
shared `call_arena` mid-flight. Using local arenas prevents this.
2026-06-01 18:33:32 +02:00
Adrià Arrufat
8fbf63fdc9 runtime: fix strict-mode calls and drain microtasks
Use the current isolate context instead of the receiver's context
when invoking primitives, fixing failures in strict-mode scripts.
Also, explicitly perform a microtask checkpoint after running a
script to ensure promise continuations run.
2026-06-01 18:22:40 +02:00
Francis Bouvier
e59c4b2cae Merge pull request #2598 from lightpanda-io/agent-remove-autoheal
refactor: remove legacy PandaScript self-healing and execution
2026-06-01 17:46:31 +02:00
Adrià Arrufat
044b34d228 refactor: remove legacy PandaScript self-healing and execution
Removes the `--self-heal` CLI option, the `scriptStep` and `scriptHeal`
MCP tools, and associated verification/iterator machinery. Replaces
"PandaScript" terminology with "slash commands" and moves shared
helpers to `tools.zig`.

BREAKING CHANGE: The `--self-heal` CLI flag and the `scriptStep` and
`scriptHeal` MCP tools have been removed.
2026-06-01 17:29:24 +02:00
Adrià Arrufat
cc6ad4d436 refactor: simplify enum usage and remove redundant helpers
- Use `std.enums.values` and `@tagName` in `ScriptRuntime` to eliminate
  the manual `primitive_specs` and `console_specs` arrays.
- Simplify `Primitive.tool()` using `std.meta.stringToEnum`.
- Clean up `writeConsoleLine` and remove `writeJsPositional`.
2026-06-01 16:26:42 +02:00
Adrià Arrufat
dd096cd0c5 Merge branch 'agent' into agent_script_js 2026-06-01 15:57:49 +02:00
Francis Bouvier
8824174afa agent: run recorded scripts as JavaScript
Replace recorded agent replay with a standalone JavaScript script runtime.
Install synchronous agent primitives in an isolated V8 context, add console
output, return structured extract values as JS objects/arrays, and route script
execution through the new runtime.

Update recording to emit .js function calls, default /save filenames to .js,
drop script-level extract save support, and refresh agent docs/tutorials for
the new format.

Self-healing is disabled for now.
2026-06-01 15:43:37 +02:00
Adrià Arrufat
2104de8e6d agent: move slash command helpers to Terminal
Moves `printHelpSection` and `printSlashParseError` from `Agent` to
`Terminal` to consolidate terminal rendering logic.
2026-06-01 15:10:09 +02:00
Adrià Arrufat
03e96d9e8f agent: extract settings and use shared utf8 truncation
Extracts provider and model settings logic from `Agent.zig` into a new
`settings.zig` module. Replaces custom UTF-8 truncation logic with
`truncateUtf8` from `string.zig`. Also updates the `zenai` dependency.
2026-06-01 15:03:42 +02:00
Adrià Arrufat
e048b4b293 Merge branch 'main' into agent 2026-06-01 12:36:08 +02:00
Adrià Arrufat
38531e1bb5 agent: deduplicate LLM setup hints and checks
Extracts the API key and LLM setup hint strings into reusable constants.
Also refactors the REPL to use the `requireLlm` helper instead of
duplicating the LLM check logic.
2026-06-01 12:32:32 +02:00
Adrià Arrufat
1a7e70fff7 agent: clean up terminal and slash command logic
- Move `atLeast` helper to `AgentVerbosity` enum.
- Simplify `ghostFirstMatch` signature by removing `anytype`.
- Replace pointer-mutating `skipWs` with `skipWhitespace`.
- Convert several comments to doc comments.
2026-06-01 12:19:03 +02:00
Pierre Tachoire
9712e4171e Merge pull request #2590 from lightpanda-io/SSO_bridge_coercion
Coerce value to strings on string.String (SSO) target
2026-06-01 10:52:04 +02:00
Karl Seguin
ec6748e3ef Merge pull request #2566 from lightpanda-io/import_maps
Improve ImporMap
2026-06-01 15:47:03 +08:00
Karl Seguin
da6d821598 Coerce value to strings on string.String (SSO) target
When `[]const u8` is a web api parameter, we'll coerce the value to a string,
e.g. true -> "true". This is correct for almost every API. APIs can also opt
to use a string.String (or string.String.Global). This is purely meant as an
optimization, but its behavior is currently different than `[]const u8` as the
bridge will only map actual JS strings to it.

This commit makes String and []const u8 behave the same, so that the only
decision as to which to use is about performance.

(APIs that strictly want a string should use js.String or js.Value and do the
type check)
2026-06-01 15:29:32 +08:00
Karl Seguin
9eb229a963 Improve ImporMap
This is driven by import-maps WPT tests. It generally does 3 high level
additions, plus various small compliance tweaks.

1 - Entries with a trailing match are used for prefix matching
2 - Supports scopes (which are entries group into a specific route-like prefix)
3 - Because of the above, resolves in correct order with fallback

Resolution is based on longest-match wins, so it doesn't require a fancy
data structures. We use ordered (by length) slices, and just iterate until we
find a match. Neither our parsing nor our matching is super efficient. While
a page might have hundreds of scripts, it likely has only 0-1 import maps and
relatively few values.

ImportMap.resolve always returns the final URL. So even if a match isn't found
based on the parsed JSON, it'll return the URL.resolve(base, url). Just to make
WPT tests pass, we do have to track invalid entries in the ImportMap, e.g.
"key": "not-a-url". In the previous version, we'd fallback to URL.resolve(base,
url). Now we return null and leave it to the caller to decide.
2026-06-01 15:24:53 +08:00
Karl Seguin
fd228a65f0 Merge pull request #2575 from lightpanda-io/url_tweaks
Improve WPT /url/ tests
2026-06-01 15:18:58 +08:00
Pierre Tachoire
ba9a1a7c72 Merge pull request #2588 from lightpanda-io/about_blank_base_url
An about:blank iframe/popup inherits its parent's base_url
2026-06-01 09:05:38 +02:00
Pierre Tachoire
c5eaf7fb5d Merge pull request #2589 from lightpanda-io/navigator_getBattery
Remove the navigator.getBattery dummy implementation
2026-06-01 09:05:24 +02:00
Pierre Tachoire
1baa8de09d Merge pull request #2585 from lightpanda-io/cookie_store_tweaks
Remove CookieListItem Interface
2026-06-01 09:04:35 +02:00
Adrià Arrufat
204e3aa31b Merge branch 'main' into agent 2026-06-01 08:04:44 +02:00
Karl Seguin
295bcf5cda Merge pull request #2583 from jschaf/codex/fix-relative-url-fragment
browser/URL: ignore query and fragment slashes in URL resolve
2026-06-01 13:07:06 +08:00
Karl Seguin
a937d559de Remove the navigator.getBattery dummy implementation
Firefox currently doesn't implement this API. Sites already have to check if
it exists, e.g `navigator.getBattery == undefined`. Implementing the method
but making it fail, it's the worst of both wolds.
2026-06-01 12:58:13 +08:00
Karl Seguin
644cea4ea9 An about:blank iframe/popup inherits its parent's base_url 2026-06-01 12:00:31 +08:00
Karl Seguin
b17ff37f8d Merge pull request #2587 from lightpanda-io/websocket_cookies
Websocket cookies
2026-06-01 11:06:19 +08:00
Karl Seguin
14f1ef35f1 URL.isHTTPs -> URL.isSecure and consider wss://
Use conn.setCookie in websocket
2026-06-01 08:35:19 +08:00
Adrià Arrufat
668f8a07dc test: add extract follow test fixtures 2026-05-31 18:49:29 +02:00
Adrià Arrufat
cf65a00a8f extract: add declarative follow option
Allows fetching sub-pages per row and resolving nested fields against
them. Supports string templates with sibling placeholders (e.g., `{id}`)
and element-specs. Updates the JS walker to be async.
2026-05-31 17:07:42 +02:00
Adrià Arrufat
85facd2fc7 eval: auto-serialize non-JSON values in save 2026-05-31 16:31:06 +02:00
Adrià Arrufat
fc15027100 eval: clarify automatic JSON serialization
Update agent docs and tool descriptions to note that objects and
arrays are automatically serialized to JSON, making manual
`JSON.stringify` calls unnecessary.
2026-05-31 16:27:28 +02:00
Adrià Arrufat
ab3deec523 eval: serialize returned objects as JSON
Objects and arrays returned from eval now serialize to JSON instead of
"[object Object]". Native errors and functions retain their string form.
2026-05-31 16:19:34 +02:00
Adrià Arrufat
27c2fe00c7 eval: support top-level await and return
Falls back to compiling the script inside an async IIFE if the initial
block-scoped compilation fails. This enables top-level await and return
statements directly in the eval tool.
2026-05-31 16:10:42 +02:00
Adrià Arrufat
63f2706202 terminal: remove bare command validation
Stop highlighting bare tokens matching tool names as errors. Also
updates the zenai dependency.
2026-05-31 15:29:53 +02:00
Adrià Arrufat
0d22bd32a5 build: bump zenai dependency 2026-05-31 15:25:56 +02:00
Adrià Arrufat
7204de5a1b agent: prompt for provider selection when multiple found
Allows interactive selection of an LLM provider when multiple keys are
detected in the environment. Saves the choice to `.lp-agent.zon`.
2026-05-31 14:33:57 +02:00
Adrià Arrufat
de0eff05f6 terminal: simplify interactive choice selection
Remove number typing input and only support arrow keys and Enter.
2026-05-31 13:21:44 +02:00
Francis Bouvier
99ef54a557 agent: add arrow-key navigation to provider picker
Make the numbered agent choice prompt interactive on TTYs: Up/Down
moves the selected row, Enter confirms it, and numeric input still works as before.

Keep the line-based numbered prompt as the non-interactive fallback, restore
terminal settings after the raw-mode picker exits, and render raw-mode output
with CRLF so menu rows stay aligned. Dim the picker hint text to match existing
terminal command hints.
2026-05-31 13:21:44 +02:00
Karl Seguin
5181cf4a14 zig fmt 2026-05-31 18:50:43 +08:00
Karl Seguin
7e0cc672be Remove CookieListItem Interface
CookieStore get/getAll return a play JS object, not an WebAPI interface.
`typeof CookieListItem === undefined` in browsers. This removes the interface
and returns the CookieListItem as a plain object.

Also adds various name/value validation based on WPT tests
2026-05-31 18:48:20 +08:00
Joe Schafer
bf1b5e5506 browser/URL: ignore query and fragment slashes in URL resolve
Resolve relative request URLs against only the path component of the
base URL. Previously, URL.resolve searched for the last slash in the
whole base URL after the authority, so slashes inside a query string or
hash route could be mistaken for path directory separators.

The reported failure was a hash-routed page loaded at
http://127.0.0.1:8123/#/login. When that page ran
fetch("api/users/login", { method: "POST" }), browser-compatible URL
resolution should have requested
http://127.0.0.1:8123/api/users/login. Instead, Lightpanda 0.3.1
reported the response URL as
http://127.0.0.1:8123/#/api/users/login, and the HTTP server received
POST /.

That meant the server returned the single-page app shell instead of the
JSON login response. The failure broke a RealWorld/Conduit-style SPA
benchmark after login because the app used relative API URLs such as
api/users/login. Changing the benchmark fixture to root-absolute API
URLs, such as /api/users/login, worked around the benchmark failure, but
left Lightpanda incompatible with normal browser behavior for relative
request URLs on hash-routed pages.

The same issue was not limited to fragments. A base URL such as
https://example/app/page?next=/foo/bar also contains slashes after the
path component. Those slashes must not affect how path-relative inputs
such as api/users/login or ../api/users/login are merged with the base
path.

This change bounds the directory calculation at the first ? or # in the
base URL and searches for the last path slash only inside that path
component. Root-absolute inputs still keep only the scheme and
authority, query-only inputs still replace the query on the current
path, and fragment-only inputs still replace the fragment on the
current path.

This matches the behavior of the WHATWG URL algorithm, Chromium's
relative URL canonicalization, Node's WHATWG URL implementation, Go's
net/url ResolveReference, and Python's urllib.parse.urljoin for the
cases covered here. All of those implementations split the base URL into
components before merging a path-relative reference, so slashes in the
query or fragment do not change the base directory.

The URL resolver tests now cover the original hash-route repro, slashes
inside query strings and fragments, host-only bases with query or
fragment components, dot-segment paths, query-only references, and
fragment-only references. The fetch Web API tests also move a page to
/#/login and POST fetch("xhr"), expecting the request URL to resolve to
http://127.0.0.1:9582/xhr rather than the hash route.

Verified with:

mise x zig@0.15.2 -- zig fmt --check ./*.zig ./**/*.zig
mise x zig@0.15.2 rust@stable -- make ZIG=zig test
2026-05-31 00:44:33 -07:00
Karl Seguin
760ac00580 Merge pull request #2579 from lightpanda-io/custom_element_define_reentrancy
Fix potential segfault in CustomElement definition
2026-05-31 07:28:18 +08:00
Adrià Arrufat
88fdeeade8 refactor: extract JSON formatting and timeout helpers 2026-05-30 23:48:50 +02:00
Adrià Arrufat
53ba47cbec agent: suggest closest slash command on typo
Implements Levenshtein distance-based suggestions for unknown slash
commands. If a typo is within two edits of a valid command, the
terminal suggests it with "Did you mean ...?".
2026-05-30 23:40:34 +02:00
Adrià Arrufat
e42862e544 terminal: add ghost hints for slash commands 2026-05-30 23:26:40 +02:00
Adrià Arrufat
58caf9faf7 agent: pretty-print JSON command results
Re-indents JSON output with 2-space indentation for better readability
in the terminal, while keeping non-JSON output unchanged.
2026-05-30 23:12:54 +02:00
Adrià Arrufat
2eb995e0ee links: return structured link objects with text and node ID
Updates `collectLinks` to return a `Link` struct containing the href,
visible text, and backend node ID. The links tool now outputs JSON.
2026-05-30 22:55:53 +02:00
Adrià Arrufat
ee96d8e813 browser: report timeout status in goto tool
Adds `WaitResult` to track whether a wait completed or timed out.
Updates the goto tool to report when a timeout occurs.
2026-05-30 22:41:24 +02:00
Tom Clarke
2ecf9ced5d Send cookies on WebSocket upgrade requests
The WebSocket upgrade handshake is an HTTP/1.1 request (RFC 6455 §4.1)
and follows ordinary cookie semantics — RFC 6265 §5.4 attaches matching
cookies to "any HTTP request" by domain/path. Without this, cookie-
authenticated WebSocket endpoints (anything session-gated, e.g. Phoenix
LiveView) reject the upgrade because their auth cookie never arrives.

Read matching cookies from the session jar with the same opts shape
HTTPDocument uses (`is_http: true, is_navigation: false`), and add a
`Cookie:` request header on the upgrade if any apply.

The TestWSServer captures the upgrade's Cookie header and exposes it
to fixtures via a new `get-cookie` command. A `cookies_on_upgrade`
fixture in websocket.html sets `document.cookie` then asserts the
server received it on the upgrade.
2026-05-30 16:37:05 -04:00
Adrià Arrufat
13e3de4b26 browser: floor remaining wait timeout to 1ms
Ensures `waitForSelector` and `waitForScript` perform at least one
check even if the timeout is 0 or already elapsed.
2026-05-30 22:34:03 +02:00
Adrià Arrufat
1ec65a00fb agent: improve command error messages
Suggest `/help` on unknown slash commands and handle `FrameNotLoaded`
by prompting the user to run `/goto <url>` first.
2026-05-30 22:31:18 +02:00