pi-agent-browser-native 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +18 -6
  3. package/dist/extensions/agent-browser/index.js +39 -16
  4. package/dist/extensions/agent-browser/lib/batch-lifecycle.js +6 -3
  5. package/dist/extensions/agent-browser/lib/browser-transcript.js +2 -1
  6. package/dist/extensions/agent-browser/lib/native-output.js +2 -1
  7. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +2 -8
  8. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +12 -2
  9. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +58 -7
  10. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +18 -12
  11. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +33 -24
  12. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +111 -53
  13. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +32 -20
  14. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +37 -32
  15. package/dist/extensions/agent-browser/lib/orchestration/native-session-defaults.js +26 -8
  16. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +3 -1
  17. package/dist/extensions/agent-browser/lib/page-target-validation.js +10 -3
  18. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +2 -1
  19. package/dist/extensions/agent-browser/lib/playbook.js +2 -2
  20. package/dist/extensions/agent-browser/lib/process-environment.js +12 -3
  21. package/dist/extensions/agent-browser/lib/process.js +6 -6
  22. package/dist/extensions/agent-browser/lib/read-confirmation.js +56 -16
  23. package/dist/extensions/agent-browser/lib/results/confirmation.js +3 -0
  24. package/dist/extensions/agent-browser/lib/results/envelope.js +5 -1
  25. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +3 -0
  26. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +34 -1
  27. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +5 -1
  28. package/dist/extensions/agent-browser/lib/results/presentation.js +15 -2
  29. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +5 -5
  30. package/dist/extensions/agent-browser/lib/runtime.js +5 -1
  31. package/dist/extensions/agent-browser/lib/session-page-state.js +35 -16
  32. package/docs/ARCHITECTURE.md +5 -5
  33. package/docs/COMMAND_REFERENCE.md +13 -9
  34. package/docs/RELEASE.md +6 -5
  35. package/docs/REQUIREMENTS.md +1 -0
  36. package/docs/SESSION_STORAGE.md +1 -1
  37. package/docs/SUPPORT_MATRIX.md +9 -3
  38. package/docs/TOOL_CONTRACT.md +20 -14
  39. package/package.json +4 -4
@@ -194,7 +194,7 @@ The 0.27.3 rebaseline is an install-only compatibility update: upstream changed
194
194
 
195
195
  Use `agent_browser` for one native command and `batch --bail` with JSON-array stdin for a fixed sequence. Use `agent_browser_code` for loops, branches, and aggregation over the same persistent browser. Return to the model when observation needs judgment. All native command families remain available; there is no public job DSL or isolated script input.
196
196
 
197
- `agent_browser` accepts `{ args, stdin?, outputPath?, timeoutMs?, sessionMode? }`. Tokens exclude the binary and shell syntax. Stdin is accepted for native batch, `eval --stdin`, and `auth save --password-stdin`. Omit `--json` for prose; include it for a parseable canonical observation. Help/version remains native text.
197
+ `agent_browser` accepts `{ args, stdin?, outputPath?, timeoutMs?, sessionMode? }`. Tokens exclude the binary and shell syntax. Stdin is accepted for native batch, `eval --stdin`, and `auth save --password-stdin`. Omit `--json` for prose backed by structured upstream JSON; include `--json` or `--json true` for a parseable canonical observation. Explicit `--json false` preserves native text for standalone commands and batch, including leading/trailing whitespace and line breaks in data, complete spills and exports, without trusted row/confirmation/page-target/artifact receipts. `outputPath` success/failure notices append prose without reparsing JSON-looking page text. Repeated flags follow upstream's last-wins semantics; batch-row flags remain native operands and do not select outer output mode. See [the output contract](TOOL_CONTRACT.md#wrapper-json). Help/version remains native text. Empty stdin batch rows (`[]`) pass through unchanged and native execution skips them; `[""]` still fails as an unknown command. `--bail` stops at that failure, while a non-bail batch retains later native results. A nonzero process exit that supplies stderr instead of JSON retains that native error and redaction, with parse diagnostics secondary. Zero-exit malformed JSON remains a strict failure.
198
198
 
199
199
  ```json
200
200
  { "args": ["open", "https://example.com"] }
@@ -340,7 +340,7 @@ Examples:
340
340
  { "args": ["snapshot", "-i"] }
341
341
  ```
342
342
 
343
- The optional `agent_browser_action` tool preserves stable accessible click/check/fill and unique visible dropdown selection. Enable `action`, then pass flat locator/selector fields. Role/name or label select resolves one current visible native combobox/listbox; it does not assume native `find ... select`. A `session` field targets a named browser. `compiledSemanticAction` remains the audit metadata name; `effectiveArgs` identifies the executed ref/command. `uncheck` remains native argv. Selector failures retain exact ref/role candidates and session-aware recovery.
343
+ The optional `agent_browser_action` tool preserves stable accessible click/check/fill and unique visible dropdown selection. Enable `action`, then pass flat locator/selector fields. Role/name or label select resolves one current visible native combobox/listbox from a fresh snapshot or the eligible next-call confirmed capture; missing or ambiguous select targets still fail. It does not assume native `find ... select`. A `session` field targets a named browser. `compiledSemanticAction` remains the audit metadata name; `effectiveArgs` identifies the executed ref/command. `uncheck` remains native argv. Selector failures retain exact ref/role candidates and session-aware recovery.
344
344
 
345
345
  For desktop, contenteditable, or host-controlled rich inputs, treat a semantic `fill` miss or mismatch differently. Active-session role/name fills can execute through one exact current editable `combobox`, `searchbox`, or `textbox` ref before upstream `find` runs. If a later selector miss still finds an exact current editable ref (`searchbox` or `textbox`), `details.richInputRecovery` and visible `Rich input recovery` describe the candidate and append `focus-current-editable-ref*` / `click-current-editable-ref*` next actions. Those actions deliberately do **not** copy the fill text and never press `Enter` or submit. Direct `fill @ref <text>` on contenteditable refs may also append/prepend instead of replacing; when the latest snapshot proves the target is contenteditable, the wrapper verifies `get text` after a successful fill and appends `details.fillVerification` plus `inspect-after-fill-verification` / `verify-filled-value` if the visible text does not match. Use the safe ladder instead: refresh refs, choose the current editable `@ref`, focus or click it, then use `keyboard type` for framework-controlled editors that require real key events. `keyboard inserttext` is paste-like: it can change a DOM value without updating application state, so use it only when later application-state evidence proves the edit was accepted. Do not auto-submit unless the user flow explicitly calls for it.
346
346
 
@@ -348,7 +348,7 @@ Do not assume Playwright selector dialects such as `text=Close` or `button:has-t
348
348
 
349
349
  Treat `@eN`, `eN`, and `ref=eN` selector refs as page-scoped. Ref-looking fill/type text, select values, paths, and keyboard/mouse data remain literal. After a successful `snapshot`, the wrapper records the latest refs and page target for that session; getter or mutation ref commands such as `get text @e4`, `click @e4`, `select @e5 chocolate`, or batch steps with old refs fail with `failureCategory: "stale-ref"` when the page target changed or the ref is absent from the latest same-page snapshot. If a session `snapshot -i` fails with `No active page`, the wrapper invalidates prior refs for that session; later mutation-prone `@e…` calls fail before upstream until a successful fresh `snapshot -i` records refs again. Inside `batch` stdin JSON, the wrapper also walks steps in order before spawn: steps whose first token can navigate or mutate set a latch; a later step whose first token is `snapshot` clears that latch for following rows; guarded steps that still mention `@e…` after an uncleared latch fail with the same `stale-ref` bucket without launching upstream. Same-snapshot form fills and native form-control steps are allowed before a click or submit step, so `fill`, `check`/`uncheck` checkbox or radio refs, checkbox/radio `click`/`tap` refs, `select` combobox refs, then a final submit `click` can run from one snapshot. Split dynamic or autosubmit forms with a fresh snapshot if a control interaction rerenders the targets. Follow the `refresh-interactive-refs` next action (it includes `--session <name>` when needed) and prefer stable `find` or `agent_browser_action` locators when navigation or rerendering is likely. Contract detail: [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) (`refSnapshot`, `refSnapshotInvalidation`).
350
350
 
351
- A successful `click` result means upstream reported a target, not that the app definitely handled the event. For top-level non-Electron direct clicks on `xpath=` targets and eligible current `@e…` refs, the wrapper installs a bounded target-specific DOM-event probe when it can; when upstream reports success but no trusted event reaches the resolved target, it fails the tool and exposes `details.clickDispatch` plus a `Click dispatch diagnostic` line with explicit retry/inspect next actions (no in-page click replay). Raw `find … click` locator calls are not probed because the wrapper has no concrete element before upstream resolves the locator, and document-level probes can falsely fail frame-scoped clicks. Direct `@e…` click probes are role-gated to current snapshot refs whose accessible role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`, with a unique role/name in both the saved snapshot and the live candidates. For both ref and XPath candidates, the wrapper requires native `get attr <original-selector> <marker>` to read back a per-probe temporary DOM marker on that candidate. Missing or mismatched identity (including frame/XPath scope mismatches) cleans up the probe and lets the native click run without dispatch verification. Duplicate-name refs pass through without a probe because their old ordinal does not prove target identity. If the probe evidence shows the target is outside a nested scroll container or viewport, `details.clickDispatch.scrollContainer` and `scroll-target-into-view-after-dispatch-miss` point to `scrollintoview <target>` before retry. When the workflow depends on a mutation, use `details.pageChangeSummary`, a wait, URL/text extraction, or a fresh `snapshot -i` before trusting the state; if nothing changed, retry with a current visible ref or stable selector and report the workflow issue. For static local fixtures or debugging where the user explicitly accepts scripted activation, `eval --stdin` can call `document.querySelector(...).click()` to exercise inline handlers and app code; treat that as an untrusted programmatic event, not as evidence that CDP/user-like clicking works. Respect explicit user stop boundaries yourself: if the user says to stop before a final order, post, purchase, or submit action, gather evidence from that page and do not click the final action or use scripted activation to bypass the stop. The wrapper does not infer broad business intent from prompt text; `details.promptGuard` is reserved for concrete artifact-before-close checks. `press`, `key`, `keydown`, and `keyup` accept exactly one key token; focus or click the target first, then run `press Enter` or another single-key command.
351
+ A successful `click` result means upstream reported a target, not that the app definitely handled the event. For top-level non-Electron direct clicks on `xpath=` targets and eligible current `@e…` refs, the wrapper installs a bounded target-specific DOM-event probe when it can; when upstream reports success but no trusted event reaches the resolved target, it fails the tool and exposes `details.clickDispatch` plus a `Click dispatch diagnostic` line with explicit retry/inspect next actions (no in-page click replay). Raw `find … click` locator calls are not probed because the wrapper has no concrete element before upstream resolves the locator, and document-level probes can falsely fail frame-scoped clicks. Direct `@e…` click probes are role-gated to current snapshot refs whose accessible role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`, with a unique role/name in both the saved snapshot and the live candidates. For both ref and XPath candidates, the wrapper requires native `get attr <original-selector> <marker>` to read back a per-probe temporary DOM marker on that candidate. Missing or mismatched identity (including frame/XPath scope mismatches) cleans up the probe and lets the native click run without dispatch verification. Duplicate-name refs pass through without a probe because their old ordinal does not prove target identity. Probe listeners and the window marker self-expire after the call's process-timeout budget plus the existing two-second cleanup grace if a pending decision blocks normal cleanup; collection cancels the timer. This uses no extra browser command while pending. If the probe evidence shows the target is outside a nested scroll container or viewport, `details.clickDispatch.scrollContainer` and `scroll-target-into-view-after-dispatch-miss` point to `scrollintoview <target>` before retry. When the workflow depends on a mutation, use `details.pageChangeSummary`, a wait, URL/text extraction, or a fresh `snapshot -i` before trusting the state; if nothing changed, retry with a current visible ref or stable selector and report the workflow issue. For static local fixtures or debugging where the user explicitly accepts scripted activation, `eval --stdin` can call `document.querySelector(...).click()` to exercise inline handlers and app code; treat that as an untrusted programmatic event, not as evidence that CDP/user-like clicking works. Respect explicit user stop boundaries yourself: if the user says to stop before a final order, post, purchase, or submit action, gather evidence from that page and do not click the final action or use scripted activation to bypass the stop. The wrapper does not infer broad business intent from prompt text; `details.promptGuard` is reserved for concrete artifact-before-close checks. `press`, `key`, `keydown`, and `keyup` accept exactly one key token; focus or click the target first, then run `press Enter` or another single-key command.
352
352
 
353
353
  Successful `snapshot -i` results can also surface `Possible overlay blockers` when their own refs already show dialog/alertdialog context plus close/dismiss controls, so agents can detect likely obstruction before clicking. When a **top-level** `@e…`/`ref=` click succeeds (not a `click` hidden inside a native batch tool call—the unified command must be `click`), the upstream payload includes `data.clicked`, no `details.clickDispatch` diagnostic fired for the same result, and the wrapper sees `details.navigationSummary.url` unchanged after the same normalization it uses for ref guards (**`#fragment` ignored**), it may run one extra `snapshot -i` and surface `Possible overlay blockers` plus `details.overlayBlockers` (`candidates`, `summary`, and a `snapshot` map that can refresh `refSnapshot`) when that snapshot shows strong modal context (`dialog` / `alertdialog`) **and** up to three close/dismiss-like controls; page-wide words such as privacy, sign in, or banner alone do not trigger it. The URL check compares the session’s prior pinned tab target to `details.navigationSummary.url`. CSS selector clicks do not run this overlay probe. The diagnostic is skipped if the wrapper already applied tab-focus correction or about-blank recovery on that result. Appended `inspect-overlay-state` / `try-overlay-blocker-candidate-*` entries in `details.nextActions` preserve namespace/session context (`--namespace <namespace> --session <name>` when namespaced, otherwise `--session <name>` when the session is named), same as other session-scoped follow-ups. Treat `inspect-overlay-state` as the safe first follow-up; only use a `try-overlay-blocker-candidate-*` next action when the candidate is clearly the control you intend to close. A click that upstream rejects because another element covers the target's click point (`is covered by` … `at its click point`) remains `failureCategory: "upstream-error"`, but receives the same session-aware `inspect-overlay-state` snapshot action. This includes direct `click`, `agent_browser_action` click, raw `find` clicks (including `nth` and omitted default-click actions), and failed native batch rows. That failed-click path does not retry the original click or synthesize a dismiss candidate before a fresh snapshot provides evidence; it is separate from silent input-dispatch failures.
354
354
 
@@ -523,7 +523,7 @@ For evidence-only screenshots, QA captures, or audit artifacts, save to an expli
523
523
  Wrapper result rendering is metadata-first for saved files. Image MIME types come from a bounded header read for PNG, JPEG, GIF and WebP, never from a filename suffix; missing, unreadable, unknown or truncated headers omit `mediaType`. This identifies a format, not full image validity. Inline screenshots use the same byte check and existing size limit, so a PNG saved as `.webm` still attaches as `image/png`; other artifact kinds are not auto-inlined. An artifact-producing command fails as `artifact-missing` with artifact `status: "stale"` when the reported path's `mtimeMs` falls outside the command's bounded start/end window (with two seconds of filesystem precision tolerance), including a previous recording that `record restart` claims to finalize; clearly old or future-dated evidence is never accepted as a fresh capture. A batch, whether supplied through stdin arrays or argument command strings, must use distinct explicit artifact destinations; preflight canonicalizes existing path ancestry, compares existing file identities to catch hardlinks, and applies full Unicode plus platform case folding on macOS/Windows so aliases cannot satisfy another step's verification. The same preflight prevents `outputPath` from aliasing a same-call browser artifact, follows upstream's forward option consumption and final effective `-o` / `--output` for `diff screenshot`, and treats the optional path on `network har stop` as an artifact destination; upstream ignores positional paths on `network har start`. Outer CLI artifact parsing removes upstream global flags, so direct forms such as `record --json start <path>` and `pdf --quiet <path>` retain their native destinations. Native batch rows do not run that cleanup: `pdf --quick ignored.pdf` writes to the literal path `--quick`, and `download #link --quiet ignored.bin` writes to `--quiet`. Preflight, directory preparation, presentation, and timeout evidence use those same operands, not the ignored trailing tokens. Screenshot destination parsing mirrors upstream's exact flag matching and `[selector] [path]` positional order: `--` is positional, `true` / `false` after screenshot-only `--full` / `-f` remain positional, extra positionals are ignored after the path slot, selector-prefixed (`.`, `#`, `@`) or uppercase-extension single arguments remain selectors, and lowercase image extensions or slash-bearing arguments are paths. The wrapper deliberately keeps its existing slash-bearing hidden-workspace path normalization (for example `.dogfood/run/foo.png`) before launch. `wait --download` is observational and may verify a download that completed just before the wait began, so it is exempt from the command-window mtime gate; an explicit wait destination, in long `--download <path>` or short `-d <path>` form (including `wait --download --timeout 30000 capture.csv`), still participates in active-recording reservation preflight; the path is the next retained operand after the first timeout pair is removed; unsupported `--download=<path>` fails with split-argument guidance:
524
524
  - screenshots return a saved-path summary, visible artifact metadata, structured `details.artifacts` metadata, and an inline image attachment when safe; the visible block includes artifact type, requested path, absolute path, existence, size, cwd, session, and repair/copy status when applicable
525
525
  - downloads, PDFs, `wait --download` files, `state save` state files, diff screenshot output images, traces, CPU profiles, completed video recordings from `record stop`, and path-bearing HAR captures return concise saved-path summaries plus structured `details.artifacts` metadata without inlining large files
526
- - `record start <path>` and `record restart <path>` report `successCategory: "artifact-pending"` and that output will be written on `record stop`; dispatched `record start` and URL-bearing `record restart` attempts append one `Page state:` warning on success or failure, describing conservative ref invalidation rather than an observed page change; explicit `--json` puts that warning in `warnings`. Only reached batch rows qualify, not preflight failures, missing binaries, help calls or unconfirmed planned rows — the wrapper invalidates the session’s prior ref snapshot (direct calls and batch steps alike, and even when the start fails with `Recording already active`, to protect older supported natives that can swap the page before that check), so old `@e…` refs fail as `stale-ref` until a fresh `snapshot -i` succeeds; `record restart <path> <url>` navigates the current page and invalidates refs the same way, while a restart without a URL, including FPS-only options, keeps the current page and refs; `details.artifacts` / `details.artifactVerification` mark that future file as `pending` with `recordingState: "openRecording"` and `willExistOnStop: true`, and `details.nextActions` includes exact `stop-pending-recording` args. When `record restart` returns a native `previousRecording`, that receipt's outcome and capture window control the previous artifact's verification; a legacy file without a terminal native receipt remains unverified, not saved; a missing or stale prior file fails as `artifact-missing` while the new recording remains visible as pending and the prior manifest row is retired. Within one Pi extension process, an unbounded transcript-backed index reserves active recording destinations independently of the bounded artifact manifest. Artifact lifecycle calls and result `outputPath` writes serialize around that global check; reservations use canonical namespace/session identity, survive manifest eviction and branch replay, and retire after direct, ordered nested-batch, fresh-replacement, code, Electron, or shutdown close; the newest pending row per identity is authoritative. Legacy batch replay retires a pending manifest only when the ordered close lifecycle leaves recording closed; a later successful browser reactivation plus `record start` keeps the new pending reservation. Lexical, hardlink, existing/dangling symlink, full Unicode-fold, and macOS/Windows case aliases are rejected, so `record restart` must use a distinct new path. Do not place `record start` or `record restart` after `close` / `quit` / `exit` in one batch: wrapper preflight rejects it because upstream can report success without starting a recording; split the close and recording into separate calls. A `No recording in progress` stop failure checks the matching native receipt once and preserves checked file metadata instead of assuming the path is missing; a later successful batch recording row opens its new pending path normally. Recovery offers an exact status query, and a stop only when the matching take is still current and pending. The target remains unverified until recording stops. Native 0.37 checks `ffmpeg` before starting; older supported natives may defer failure. If a successful start/restart reports pending output without `ffmpeg`, the wrapper appends `Recording dependency warning: ffmpeg not found on PATH` and `details.recordingDependencyWarning`; stop, check the result, then install the dependency before starting a new recording.
526
+ - `record start <path>` and `record restart <path>` report `successCategory: "artifact-pending"` and that output will be written on `record stop`; dispatched `record start` and URL-bearing `record restart` attempts append one `Page state:` warning on success or failure, describing conservative ref invalidation rather than an observed page change; explicit `--json` puts that warning in `warnings`. Only reached batch rows qualify, not preflight failures, missing binaries, help calls or unconfirmed planned rows — the wrapper invalidates the session’s prior ref snapshot (direct calls and batch steps alike, and even when the start fails with `Recording already active`, to protect older supported natives that can swap the page before that check), so old `@e…` refs fail as `stale-ref` until a fresh `snapshot -i` succeeds; `record restart <path> <url>` navigates the current page and invalidates refs the same way, while a restart without a URL, including FPS-only options, keeps the current page and refs; `details.artifacts` / `details.artifactVerification` mark that future file as `pending` with `recordingState: "openRecording"` and `willExistOnStop: true`, and `details.nextActions` includes exact `stop-pending-recording` args. When `record restart` returns a native `previousRecording`, that receipt's outcome and capture window control the previous artifact's verification; without that receipt, only an acknowledged native `restarted: true` retires the previous take, whose file remains unverified, not saved; rejected or pending restarts keep the active take's reservation and stop action; a missing or stale prior file fails as `artifact-missing` while the new recording remains visible as pending and the prior manifest row is retired. Within one Pi extension process, an unbounded transcript-backed index reserves active recording destinations independently of the bounded artifact manifest. Artifact lifecycle calls and result `outputPath` writes serialize around that global check; reservations use canonical namespace/session identity, survive manifest eviction and branch replay, and retire after direct, ordered nested-batch, fresh-replacement, code, Electron, or shutdown close; the newest pending row per identity is authoritative. Legacy batch replay retires a pending manifest only when the ordered close lifecycle leaves recording closed; a later successful browser reactivation plus `record start` keeps the new pending reservation. Lexical, hardlink, existing/dangling symlink, full Unicode-fold, and macOS/Windows case aliases are rejected, so `record restart` must use a distinct new path. Do not place `record start` or `record restart` after `close` / `quit` / `exit` in one batch: wrapper preflight rejects it because upstream can report success without starting a recording; split the close and recording into separate calls. A `No recording in progress` stop failure checks the matching native receipt once and preserves checked file metadata instead of assuming the path is missing; a later successful batch recording row opens its new pending path normally. Recovery offers an exact status query, and a stop only when the matching take is still current and pending. The target remains unverified until recording stops. Native 0.37 checks `ffmpeg` before starting; older supported natives may defer failure. If a successful start/restart reports pending output without `ffmpeg`, the wrapper appends `Recording dependency warning: ffmpeg not found on PATH` and `details.recordingDependencyWarning`; stop, check the result, then install the dependency before starting a new recording.
527
527
  - `batch` keeps each step's artifacts in `details.batchSteps[].artifacts`; top-level `details.artifacts` and `details.artifactManifest` coalesce an earlier pending recording into the later saved, missing, or stale terminal result for the same namespace/session identity; a successful later close retires an unfinalized recording as `close-abandoned`, uses `missing` only after a filesystem check proves absence (otherwise unverified), removes its stop action, and resets earlier ref/page/network-route batch state; a later successful `record stop` replaces that intermediate abandoned row with its verified saved artifact, and later rows—including failed rows—whose lifecycle reports a browser launch may rebuild state without triggering stale pre-close `about:blank` recovery; failed-step `batchSteps[]` retains only the bounded `lifecycle.effectiveLaunch.browserLaunched` boolean for replay, explicitly non-launching diagnostics leave the close terminal, missing lifecycle evidence remains conservatively active even on the first managed call, every successful close clears wrapper trace/profiler ownership before ordered later successful rows can rebuild it, namespace-scoped `close --all` clears all matching managed/attached/page/ref/route/trace/recording ownership, and any later same-session failure before recording stops keeps exact `stop-pending-recording` args alongside its normal recovery
528
528
 
529
529
  `diff screenshot` follows the file-artifact path above for the **diff** image: model-visible text and `details.artifacts` focus on that output, while baseline paths stay out of the artifact summary block, and Pi does **not** auto-inline the diff the way it inlines trusted `screenshot` captures. `state load` may print the loaded path in prose but does not add a saved-file artifact entry the way `state save` does.
@@ -589,7 +589,9 @@ Use `tab list` and `tab <tab-id-or-label>` when a profile restore, pop-up, or cl
589
589
 
590
590
  ### Recover from guarded-action confirmations
591
591
 
592
- When a call uses `--confirm-actions` and upstream requires confirmation, the native tool result prints the pending confirmation id and both recovery calls. Use the same `agent_browser` tool; do not switch to bash.
592
+ Configure `--confirm-actions` on the session's first command. The extension retains that setting for the same canonical namespace/session across helpers, bare follow-ups, code and advanced calls, and transcript resume. Follow the exact returned confirm/deny action without repeating the policy. First launch and new explicit `--confirm-actions` / `--config` selections use native argv → environment → config precedence. Bare follow-ups retain an established string, including explicit `""`, over ambient environment/discovered-config edits; those defaults apply again after close or for a new identity. Native unset is not pinned and continues ordinary native default resolution. Explicit policy changes still use native restart behavior and can discard pending confirmations. Successful close, namespace-wide close, and successfully closed managed replacements retire the setting with that session. Replacement cleanup uses the old identity's setting; failed or pending close retains its continuity state. Electron launch and host probes use the selected setting too. A definite wrapper/native CLI parse/config/local-inspection rejection restores the previous setting unless an earlier browser helper could have established the request; interrupted work retains selected intent.
593
+
594
+ When upstream requires confirmation, the native tool result prints the pending confirmation id and both recovery calls. Confirming one action in a compound command can return another native confirmation; follow its returned action too. Pending navigation is intent, never an observed page target, including pending native batch rows. A completed compound confirmation reconciles its native resulting target before ordinary follow-ups. A tab-creation transport can add `after_confirmation: ["open", <requested URL>]` plus nonempty `guidance` when the URL matches the original `tab new` request, including a preceding `--label`. Follow that visible continuation only after approval successfully creates the blank/labeled tab, in the same session and new tab—never after denial. The extension preserves the exact decision and conditional guidance but does not execute the continuation. Confirmed full snapshots install returned refs in direct and batch row order. A final confirmed capture supplies the next same-identity call's freshness sample once, including role click/check/fill and unique accessible select resolution; an intervening call or later row consumes the opportunity. A pending snapshot is displayed as confirmation required, never as an empty capture. Use refs from that capture: membership, target and invalidation checks still apply. On this one use, live verification compares the complete capture URL, including fragments, and rejects refs when it changes—including wrapper-owned managed sessions and warm sessions without resume. Ordinary ref/tab URL comparisons remain fragment-insensitive. DOM-only renames on the same page after the sample are not resampled on this one use. Further ref actions use normal freshness checks. Optional click-dispatch eval probes are skipped when the effective native confirmation list guards `evaluate`; native click confirmation still applies. Use the same `agent_browser` tool; do not switch to bash.
593
595
 
594
596
  ```json
595
597
  { "args": ["--confirm-actions", "click", "click", "@danger"] }
@@ -602,7 +604,7 @@ If the result says `Pending confirmation id: c_8f3a1234`, choose one follow-up:
602
604
  { "args": ["deny", "c_8f3a1234"] }
603
605
  ```
604
606
 
605
- For a policy-required explicit URL read, use the returned actions: they name the actual native namespace/session, including `default`, and this routing survives resume/branch replay. The wrapper skips page helpers on matching confirm/deny only when the native read response also advertises `capabilities.readRequiresConfirmation: true`, which includes strict native ID matching. Legacy read prompts retain correct routing but normal page checks; DOM or page-content-shaped prompts never gain the exemption. Explicit caller identities and the code cell's fixed session/namespace still win.
607
+ Use the exact returned actions for a genuine native control: they preserve its ID and canonical namespace/session across resume/branch replay. Matching guarded decisions skip page/pinning helpers that could overwrite the native pending slot, then reconcile completed browser actions normally; this is not proof of native ID validation or browser independence. Only a matching first effective guarded decision gets that suppression in native batch; native raw-argument-over-stdin selection and later page/ref checks remain unchanged. Page content and unrelated/nonmatching decisions gain no exemption. Explicit URL reads remain separate: only `capabilities.readRequiresConfirmation: true` grants browser independence with native ID checking; legacy URL reads keep normal checks. Explicit caller identities and the code cell's fixed identity still win. A pending cold-reopen helper exposes its exact action without running the requested getter or replaying open. A failed inner confirmed action fails the tool; a completed confirmed close retires session state.
606
608
 
607
609
  Confirmation context may be redacted when it contains credentials, tokens, cookies, or auth-bearing URLs. Replacements are marked `[REDACTED]` (URL-encoded in parsed URLs); ordinary technical phrases such as `bearer token` and `bearer authentication` stay intact outside credential fields and headers. URL scrubbing covers `code`, SAMLRequest, SAMLResponse, RelayState, `authorization_session_id`, and auth-context `state` / `nonce` while retaining ordinary non-auth query values and the spelling of URLs needing no redaction. Visible text, structured details, persisted spills, and `outputPath` exports use the same redaction; exact internal page-target URLs remain available to browser state logic. Use the id exactly as printed.
608
610
 
@@ -761,6 +763,8 @@ These calls return plain text and stay stateless: the extension does not inject
761
763
 
762
764
  `cookies set --curl <file>` selects native file-import mode even when `--curl` follows other set operands. Ordinary import paths remain visible in invocation echoes and failed batch diagnostics; inline cookie values, sensitive flags, and secret-bearing path text still redact. File contents are not echoed by this exception.
763
765
 
766
+ With explicit `--json false`, cookie `name=value` (including empty names) and storage `key: value` (including empty and colon-containing keys) output use command-aware redaction before presentation and export; benign preferences remain visible. Standalone `storage local|session get <key>` and shorthand `storage local|session <key>` redact the whole sensitive value, including embedded newlines, even when invocation display redaction changes the supplied key. All-entry/raw-batch text has unescaped multiline values and no row provenance, so use structured JSON for unambiguous sensitive storage. See [the output contract](TOOL_CONTRACT.md#wrapper-json).
767
+
764
768
  Privacy note: `cookies get` can expose real profile cookies. Do not run it against `--profile Default` or other authenticated profiles unless the user explicitly needs cookie inspection; prefer task-specific page actions and storage checks.
765
769
 
766
770
  ### WebMCP page tools
@@ -1036,7 +1040,7 @@ The extension composes stock `--args --no-startup-window` for local Chrome start
1036
1040
 
1037
1041
  On Android/Termux, follow the README setup to install the packaged Linux-musl arm64 upstream binary, install Termux's `which`, and expose its launcher as `$PREFIX/bin/chromium`. Prefer that upstream system-browser discovery over ambient `AGENT_BROWSER_EXECUTABLE_PATH`: it survives isolated `HOME` values and works for ordinary native calls. Code retains the selected browser's normal launch configuration. Wrapper-generated Android managed identities use a compact 80-bit digest so ordinary namespaces and fresh rotations fit upstream's Unix socket path.
1038
1042
 
1039
- - `--no-auto-dialog`: disable automatic dismissal of alert/beforeunload dialogs. Environment: `AGENT_BROWSER_NO_AUTO_DIALOG`.
1043
+ - `--no-auto-dialog`: disable automatic dismissal of alert/beforeunload dialogs. Environment: `AGENT_BROWSER_NO_AUTO_DIALOG`. Explicit `true`/`false` reaches all helpers in that call; keep the same selection on follow-ups to avoid native daemon replacement.
1040
1044
  - `--idle-timeout <ms>`: native background browser lifecycle setting (also accepts `10s`, `3m`, `1h`). Caller-owned sessions retain native idle policy; an explicit flag is carried to every helper in that call. Keep it consistent between calls to avoid a native daemon restart. Only wrapper-owned sessions receive the implicit timeout and numeric mismatch check against `PI_AGENT_BROWSER_IMPLICIT_SESSION_IDLE_TIMEOUT_MS`.
1041
1045
 
1042
1046
  ### Lightpanda engine
@@ -1069,7 +1073,7 @@ On Android/Termux, follow the README setup to install the packaged Linux-musl ar
1069
1073
  - `--content-boundaries`: wrap page output in boundary markers. Environment: `AGENT_BROWSER_CONTENT_BOUNDARIES`.
1070
1074
  - `--max-output <chars>`: truncate page output to N characters. Environment: `AGENT_BROWSER_MAX_OUTPUT`.
1071
1075
  - `--allowed-domains <list>`: restrict browser and `read` traffic to exact or `*.` wildcard domain patterns. Environment: `AGENT_BROWSER_ALLOWED_DOMAINS`. Use a fresh local Chrome context; upstream 0.32.0 owns containment and incompatible-mode rejection and disables Chromium `RTCPeerConnection` while active. The wrapper passes the setting and result through unchanged.
1072
- - `--action-policy <path>`: action policy JSON file. Environment: `AGENT_BROWSER_ACTION_POLICY`.
1076
+ - `--action-policy <path>`: action policy JSON file. Environment: `AGENT_BROWSER_ACTION_POLICY`. Explicit policy values also reach internal helpers for the current call. Keep the action-policy path consistent on follow-ups; changing or omitting it can restart the native daemon and discard pending confirmations. The established `--confirm-actions` setting is retained for same-session follow-ups.
1073
1077
  - `--confirm-actions <list>`: action categories requiring confirmation. Environment: `AGENT_BROWSER_CONFIRM_ACTIONS`.
1074
1078
  - `--confirm-interactive`: interactive confirmations; auto-denies when stdin is not a TTY. Environment: `AGENT_BROWSER_CONFIRM_INTERACTIVE`.
1075
1079
  - `-p, --provider <name>`: provider such as `ios`, `browserbase`, `kernel`, `browseruse`, `browserless`, or `agentcore`. Environment: `AGENT_BROWSER_PROVIDER`.
@@ -1078,7 +1082,7 @@ On Android/Termux, follow the README setup to install the packaged Linux-musl ar
1078
1082
  - `--model <name>`: AI model for `chat`. Environment: `AI_GATEWAY_MODEL`.
1079
1083
  - `-v, --verbose`: show tool commands and raw output.
1080
1084
  - `-q, --quiet`: show only AI text responses.
1081
- - `--debug`: debug output. Environment: `AGENT_BROWSER_DEBUG`.
1085
+ - `--debug`: debug output. Environment: `AGENT_BROWSER_DEBUG`. Explicit `true`/`false` reaches all helpers in that call, including overriding true config defaults; `false` removes the debug variable so native presence checks cannot enable logging. Inherited env/config behavior is unchanged.
1082
1086
  - `AGENT_BROWSER_PLUGINS`: JSON plugin registry override for the upstream `plugin` commands.
1083
1087
  - `--version`, `-V`: show version.
1084
1088
 
package/docs/RELEASE.md CHANGED
@@ -21,7 +21,7 @@ Provide one concrete maintainer workflow for validating repo state, package cont
21
21
 
22
22
  The owner's 2026-09-21 **“waive windows”** instruction makes Windows qualification nonblocking for this rollout, even with available runners, and supersedes older mandatory-Windows/no-waiver wording below and in platform guidance. Preserve failed diagnostics; do not claim a Windows full-suite pass or continue Windows repair experiments. Known policy-lock/restored Electron failures and bounded repeat results are tracked in [#191](https://github.com/fitchmultz/pi-agent-browser-native/issues/191) and the [current support matrix](SUPPORT_MATRIX.md#current-0616-rollout).
23
23
 
24
- Linux/macOS, declared Node floors, official/fork host and consumer-artifact checks, and Native lifecycle Linux remain required. All three final reviewers (reviewer-gpt, reviewer-ponytail, reviewer-claude) must approve before merge, release or activation. This waiver changes neither runtime nor the composed `release`/`prepublishOnly` scripts: record individually satisfied gates and the explicit Windows waiver, never label the full composition passed.
24
+ Linux/macOS, declared Node floors, official/fork host and consumer-artifact checks, and Native lifecycle Linux remain required. This waiver changes neither runtime nor the composed `release`/`prepublishOnly` scripts: record individually satisfied gates and the explicit Windows waiver, never label the full composition passed.
25
25
 
26
26
  Docs/workflow-only changes may reuse prior live/platform evidence when the complete compiled runtime, runtime dependencies and relevant tests are unchanged. Changed published files require a new retained tarball checked by `verify-package.mjs --smoke-pi`, including runtime-only SDK and actual CLI loading. Publish those exact checked bytes without repacking.
27
27
 
@@ -276,7 +276,7 @@ These show up often in cloud dev boxes and scripted smokes; they are maintainer
276
276
  | **Pi CLI vs repo devDependencies** | Global `pi` older than the minimum Pi runtime floor for the release can change TUI behavior, `/reload`, package installs, and tool routing during lifecycle or checkout smokes. | Run `npm run doctor` and align `pi` with the current audited baseline before release gates (`pi update` or install the matching version). The published peer range stays wildcard per Pi package docs, and the doctor enforces the minimum Pi runtime floor before package validation. |
277
277
  | **npm lockfile (`packageManager`)** | `package.json` pins **npm@12.2.0** for the Node 24.21.0 runtime baseline. Older npm versions can rewrite optional-platform metadata in `package-lock.json`; private registry proxies can also leak organization URLs into `resolved` entries. | Prefer `npx -y npm@12.2.0 install` when refreshing the lockfile; do not commit unrelated lockfile churn or WorkOS/private-registry URLs. |
278
278
  | **`pi -p` / print mode** | Non-interactive `pi -p` may hang or emit no stdout for long real-browser smokes without a TTY. | Use **tmux**-driven interactive `pi` for release evidence and checkout smokes; reserve `-p` for short, non-browser checks. |
279
- | **Real-browser cleanup** | `real-upstream`, Sauce Demo, and live-site runs can leave defunct Chrome/`agent-browser` children if a session aborts mid-flow. | Close via `agent_browser` / `agent-browser` `close`, kill stray tmux sessions, and remove temp screenshots/HARs under `/tmp` or your chosen artifact dirs. |
279
+ | **Real-browser cleanup** | `real-upstream`, Sauce Demo, and live-site runs can leave defunct Chrome/`agent-browser` children if a session aborts mid-flow. | Close only test-owned browsers via `agent_browser` / `agent-browser` `close`, stop this run's tmux sessions, and remove its disposable screenshots/HARs. Preserve unrelated resources and retained evidence. |
280
280
  | **Automated prompt driving** | Grepping tmux pane text for words that also appear in the **user** prompt (`PASS`, `FAIL`, `checkout overview`, `Smoke result:`) can false-complete before the agent finishes. | Wait for pane idle (no `Working…`), `agent_browser close` / `Artifact lifecycle`, or JSONL tool results—not instruction phrases copied from the prompt. |
281
281
  | **Lifecycle verify flags** | `npm run verify -- lifecycle --model` or `--timeout-ms` without the next argv token fails fast with a usage error—the `project.mjs` facade validates passthrough the same way as `scripts/verify-lifecycle.mjs`. | Always pair flags with values (`--model openai-codex/gpt-5.5:minimal`, `--timeout-ms 600000`) or omit `--model` / `--timeout-ms` to keep the harness defaults (`zai/glm-5.2`, **180000 ms** per-step waits). |
282
282
 
@@ -306,6 +306,7 @@ This suite requires the installed stable `agent-browser --version` to meet the m
306
306
  - **Recording FPS paths and pinning:** the opt-in `test/agent-browser.batch-fidelity.test.ts` native case checks direct/raw/stdin preflight before dispatch, actual start/restart destinations and pinned targets with leading `--fps`, conservative start-ref protection, FPS-only restart continuity, and valid 12-fps WebM output after explicitly logged 12-second fixture captures. Run it with `PI_AGENT_BROWSER_REAL_UPSTREAM=1`; it requires native 0.37 or newer. Keep focused older-native controls for fresh-page starts and the start-then-ref batch latch when changing that policy.
307
307
  - **Managed session core and safe diagnostic matrix:** fresh `open` on the contract fixture, then implicit reuse across `eval --stdin`, `snapshot -i`, interaction commands (`click`, `dblclick`, `fill`, `type`, `type --clear --delay`, `focus`, `keyboard` with `type` / `inserttext`, `press`, `hover`, `check`, `uncheck`, `select`, failed `select` no-match, `upload`, `drag`, `mouse`, `scroll`, off-viewport click, `scrollintoview`, `wait` on selectors in the main frame and a selected iframe), extraction (`get` variants, `is` variants, `find label … fill` via native `<label>`, `aria-label`, and `aria-labelledby`, inline `eval`), file outputs (`screenshot`, `pdf`), navigation (`back`, `forward`, `reload`, `tab list`, another `open` to the same fixture), `batch` stdin, `pushstate`, `vitals … --json`, network route/requests/HAR, diff snapshot/screenshot/url, trace/profiler, console/errors/highlight, stream enable/status/disable, and `cookies set --curl`.
308
308
  - **Managed restore correctness and persistence:** while the restore-enabled managed daemon is active, assert raw argument and stdin batches containing nested `connect` fail before upstream spawn; a new harness replaying the owned launch result must also reject incompatible reuse of that live same-name daemon. Seed a cookie plus localStorage/sessionStorage, close the first managed browser while a conflicting parent namespace is set, verify the default-namespace daemon actually closed, create a new extension harness with the same cwd, reopen the fixture with `sessionMode: "fresh"`, and assert all three values restore before closing the second browser. On POSIX, separate isolated real-browser launches assert automatic restore stays disabled and no snapshot is written through either a symlinked `sessions` directory or a file symlink in `sessions/.tmp`; a relative `HOME`, untrusted writable HOME ancestry, and a non-Git cwd must fail closed. Verify a checkout rename preserves its generation identity but starts a fresh composite restore key (fail-closed, because the cwd-derived managed-session base name changes), a copied or path-replacement checkout gets a new key, and changing the Git-generation marker between planning and spawn prevents agent-browser from starting. Run two same-identity harnesses concurrently so a compatible launch publishes its daemon policy before a waiting incompatible launch re-inspects and fails without reaching its main spawn; also fail a fresh non-batch command after daemon creation and verify shutdown closes the retained identity.
309
+ - **Confirmation continuity:** the focused `contract suite matches confirmation launch policy surviving native nextActions` case follows exact confirm/deny actions without policy argv, checks a useful compound audit across resume followed immediately by title/body reads without a repairing URL getter, retains the native daemon PID on bare getters, and preserves an active recording and destination sentinel after denying restart. The nested recording-restart/navigation subcase requires the pinned native recording-policy patch; stock 0.38.1 skips only that subcase. With that patched binary first on `PATH`, run `PI_AGENT_BROWSER_REAL_UPSTREAM=1 PI_AGENT_BROWSER_NATIVE_COMPOUND_POLICY=1 node --import tsx --test --test-force-exit --test-name-pattern 'confirmation launch policy' test/agent-browser.real-upstream-contract.test.ts`. Both variables are test selectors, not runtime configuration. Focused read-confirmation, cold-resume, session-page-state and managed-daemon-policy owners additionally cover exact generic/native-read provenance, forged/foreign controls, completed click/tab and failed-inner action reconciliation, confirmed-close replay, definite unestablished policy requests and pending cold helper exposure. Qualify actual SDK resume plus immediate title/body follow-ups without a repairing getter; helpers must not replace the original native pending slot.
309
310
  - **Cold first-read continuity:** the focused `contract suite matches cold URL reopen after quit` case starts an explicit `sessionMode: "fresh"` managed browser, runs actual quit cleanup, waits for the exact old daemon to exit, reloads the saved branch, and requests `snapshot -i` before any explicit navigation. Empty storage and origin storage must both reach their remembered non-root URL, report fresh refs in the main frame, and retain storage without pretending to retain unsaved forms or JavaScript memory. Separate deterministic cold-resume/boundary tests cover first `get url` and history commands, non-page daemon-starting calls and batch prefixes, hash routes, pending-state transcript replay, live missing tabs versus explicit URL destinations, close retirement, and caller-owned/attached/restore-disabled boundaries. Those fake-upstream checks do not replace native-browser or real Pi lifecycle qualification of the complete candidate.
310
311
  - **Failure shape:** `react tree` on a page opened with `--enable react-devtools` but without a React app (expects a clear missing-renderer error with session-bound `details`).
311
312
  - **Async download:** `open` on the `/download` fixture, anchor-triggered export, then `wait --download <path>` metadata and wrapper artifact reporting for the requested path.
@@ -330,11 +331,11 @@ node --test test/agent-browser.native-lifecycle.test.mjs
330
331
 
331
332
  #### Routine GitHub PR checks
332
333
 
333
- [`.github/workflows/pi-compatibility.yml`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/.github/workflows/pi-compatibility.yml) runs on PRs to `main` and manual dispatch. Independent Ubuntu jobs qualify the locked official Pi graph and the maintained fork's current `main` as a complete installed graph on Node 24.21.0. Each runs `check:compat` once: host identity, typecheck, the full offline suite, and packed SDK/CLI loading. A parallel macOS Node 24.21.0 job checks the packed Pi load without repeating the full suite. When the candidate official version differs from the supported Pi floor, that macOS job also selects the actual floor graph for a focused type/package smoke. The host selector uses the pinned `fitchmultz/.github` automation scripts without changing them. There are no cross-job host artifacts or persistent caches. The official lane always qualifies the locked official graph; the fork lane first compares the fork's `main` Pi version against the supported Pi floor, and while the fork is below that floor it skips the fork build/qualification and emits a truthful unattempted-below-floor receipt in the job summary instead of failing or resurrecting dropped fork APIs. The full supported-fork qualification path runs unchanged once the fork reaches the floor.
334
+ [`.github/workflows/pi-compatibility.yml`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/.github/workflows/pi-compatibility.yml) runs on PRs to `main`, manual dispatch, and release workflow calls. It resolves the latest stable official Pi version and latest maintained fork `main` commit once, or accepts those exact identities from the enclosing release run. Independent required Ubuntu jobs qualify each selected host's complete installed dependency graph on Node 24.21.0. Each runs `check:compat` once: host identity, typecheck, the full offline suite, and packed SDK/CLI loading. A parallel macOS Node 24.21.0 job typechecks and checks the packed Pi load against the same latest official graph without repeating the full suite. The host selector uses the immutable, reviewed `fitchmultz/.github` automation scripts; that automation pin is not a Pi qualification target. Locked development snapshots and the support floor are not validation lanes, and neither host is skipped based on a floor comparison.
334
335
 
335
- [`.github/workflows/native-lifecycle.yml`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/.github/workflows/native-lifecycle.yml) runs the separate **Native lifecycle Linux** gate through [`scripts/ci-native-lifecycle.sh`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/ci-native-lifecycle.sh). It independently checks out the PR head, builds the compiled extension on Node **24.21.0**, and runs portable native SDK lifecycle checks against isolated loopback browser fixtures. It replaces the fork-only checkpoint gate and requires passing checks with no unexpected failure, cancellation or skip. It does not repeat the official full suite. Sources use a private `/tmp` root, sanitized environment, non-root execution, and a loopback-only network namespace. No personal profile, credentials, or model calls are involved.
336
+ [`.github/workflows/native-lifecycle.yml`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/.github/workflows/native-lifecycle.yml) runs the separate **Native lifecycle Linux** gate through [`scripts/ci-native-lifecycle.sh`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/ci-native-lifecycle.sh). It independently checks out the PR head, selects the latest official graph using its once-resolved version (or the exact version supplied by the enclosing release run), builds the compiled extension on Node **24.21.0**, and runs portable native SDK lifecycle checks against isolated loopback browser fixtures. It replaces the fork-only checkpoint gate and requires all 12 checks to pass with no failure, cancellation or skip. It does not repeat the official full suite. Sources use a private `/tmp` root, sanitized environment, non-root execution, and a loopback-only network namespace. No personal profile, credentials, or model calls are involved.
336
337
 
337
- The fork compatibility workflow resolves `main` once at checkout and uses that exact commit for its build and provenance checks; the native lifecycle gate instead uses the locked official Pi graph installed by `npm ci`. CI writes setup, provenance, command-reference, and native TAP results to the job log instead of uploading artifacts. The checks cover their exact source, host, and synthetic browser paths; they do not replace release/manual-publication gates, real-site authentication, or full personal-workspace restore qualification.
338
+ CI writes setup, provenance, command-reference, and native TAP results to the job log and uploads short-lived evidence: `official-host`, `fork-host`, and `macos-host` contain exact selected host receipts; `native-lifecycle` contains the host receipt, native TAP results and lifecycle transcripts. Each artifact is retained for three days. There are no persistent caches. The checks cover their exact source, host, and synthetic browser paths; they do not replace release/manual-publication gates, real-site authentication, or full personal-workspace restore qualification.
338
339
 
339
340
  ### Native Linux socket-root regression
340
341
 
@@ -73,6 +73,7 @@ Define the product requirements and constraints for `pi-agent-browser-native`.
73
73
  - Persist one canonical begin/finish event format for direct/code/host work through native Pi custom entries and a shared reducer. Define full captures once, preserve exact targets/native identity, and persist artifact-row changes rather than unchanged views. Interrupted outcomes remain unknown until inspection; failed begin blocks dispatch and failed finish stops dependent work without mutation retry. Require actual journal publication before browser dispatch. Legacy browser/lease decoders belong only to validated offline stopped-copy conversion; preserve original bytes, all ancestry and owner UUIDs. A fork must never acquire its parent's cleanup rights. Bound extension replay with token projection and winning definitions on both hosts, while disclosing stock official Pi's eager native loader and individual-value memory ceilings; see [`SESSION_STORAGE.md`](SESSION_STORAGE.md).
74
74
  - Use one canonical model-visible observation for JSON/prose/code with success/data/error, exact recovery, artifact receipts, and image observations. Native `outputSchema`/`structuredContent` under the Browser tool namespace expose that same bounded redacted projection, not raw internal details. Loader inventory and normalized search data keep their existing shapes. Details is audit/replay metadata, not the sole source of actionable failures. Return canonical native `isError` without a result-repair hook; preserve prose notices and explicit JSON. Inner calls avoid full model rendering/spills/image encoding. Bound outer output and name a complete redacted observation spill rather than truncating executable payloads.
75
75
  - Attach only verified real images; code selects current-cell image handles explicitly. Report pixel dimensions and CSS viewport/DPR/crop/scroll/frame evidence conservatively. Unknown geometry remains unknown; neither full-page nor element captures are assumed viewport coordinates. Positively identified Lightpanda images are text-rendered, never graphical CSS/mouse-coordinate evidence. Do not inject model-specific image-detail/provider payload changes.
76
+ - Explicit native text (`--json false`) must retain redacted stdout and process failures without promoting JSON-looking, confirmation-looking or URL-shaped page content into trusted receipts or session targets; structured callers/helpers remain strict JSON. Optional output booleans follow native last-wins semantics. Native empty batch rows are accepted without rewriting stdin; malformed row shapes still fail before dispatch. A failed process's stderr takes precedence over absent/invalid JSON diagnostics, while zero-exit malformed machine output stays strict.
76
77
  - Direct upstream capability remains available: explicit sessions, state/restore paths, profiles, config, file access, launch arguments, environment, local pages, output paths, and close arguments pass through under ordinary native semantics. Keep all session/state rows visible. Wrapper validation concerns input shape, lifecycle integrity, and truthful evidence, not authorization of resources.
77
78
  - Keep Chrome default and separately installed Lightpanda opt-in. Treat `--engine` as launch-scoped through existing managed fresh-launch recovery. Omit only wrapper-supplied Chrome profile/executable defaults when Lightpanda is selected; preserve native caller settings and unsupported-command errors. No new driver, engine cache, browser bundle, or automatic Chrome fallback.
78
79
  - Native download owns clicking and saving, including loopback, Blob exports, and redirects. Create artifact directories before dispatch and verify saved files; output-data writes must not alias browser artifacts. Pending recording is not saved video.
@@ -4,7 +4,7 @@ Pi's native JSONL journal owns conversation history and branch ancestry. Browser
4
4
 
5
5
  ## Events and snapshots
6
6
 
7
- Every browser-affecting direct, code, or Electron host operation records a begin before dispatch and a finish after observing its effects. With a journal, each record requires confirmed publication. Direct browser use in a disabled/no-file session (`--no-session`) uses Pi's current in-memory ancestry without claiming persistence. Code still requires a published intent journal; a named but not-yet-published persistent conversation also refuses browser dispatch. Sessionless local inspections and browser-independent reads remain available before publication, including full redacted private spills and caller-selected output files. Their observation-only artifact receipts do not require a journal; browser effects, page changes and native confirmations retain their required records. `data.event` version 1 correlates them with an operation ID, the outer tool-call ID and inner command index. The same reducer applies committed live events and replays the selected branch.
7
+ Every browser-affecting direct, code, or Electron host operation records a begin before dispatch and a finish after observing its effects. With a journal, each record requires confirmed publication. Direct browser use in a disabled/no-file session (`--no-session`) uses Pi's current in-memory ancestry without claiming persistence. Code still requires a published intent journal; a named but not-yet-published persistent conversation also refuses browser dispatch. Sessionless local inspections and browser-independent reads remain available before publication, including full redacted private spills and caller-selected output files. Their observation-only artifact receipts do not require a journal; browser effects, page changes and native confirmations retain their required records. `data.event` version 1 correlates them with an operation ID, the outer tool-call ID and inner command index. The same reducer applies committed live events and replays the selected branch. Page events also retain the selected native confirmation string (including explicit `""`; native unset is not pinned) for each canonical namespace/session, including interrupted begins. A definite unestablished completion restores the previous setting only if no earlier browser helper could have established the request. Same-identity helpers and follow-ups use it before dispatch; successful close clears it with session state. Replacement cleanup uses the old identity's setting and keeps its continuity state on failed close. New explicit `--confirm-actions` / `--config` selections retain native precedence; bare follow-ups preserve the established setting over ambient defaults. Pending confirmation commands stay solely in native daemon memory. The existing ordered confirmation marker retains only exact ID/identity and compact command/native-action provenance, including helper controls; it never stores argv or replays commands. Exact guarded decisions avoid overwriting page helpers without claiming native ID validation or browser independence. Cold pending reopen keeps the target unknown and exposes the native decision before the getter can dispatch. Pending navigation rows cannot supply an observed target; completed compound confirmations, including native batch rows, reconcile their native target before follow-ups. Successful confirmed code close records realized retirement, not an unresolved intent; attachment replay consumes that compact close effect too. Confirmed full snapshot rows feed the ordinary ordered capture fold. Only a final successful complete confirmed capture marks the existing cleared confirmation as fresh for one next same-identity call. Semantic role click/check/fill and unique accessible select resolve through that same eligible capture. The call persists consumption, including direct/code/batch replay, so resume cannot resurrect the one-use sample. Ordinary membership, target, invalidation and generation checks still apply; the one use does not resample DOM-only renames after capture.
8
8
 
9
9
  A finish explicitly reuses, replaces, invalidates, or leaves refs unknown. Each complete capture defines one `data.snapshot`: an ID, keyed ref membership and accessible metadata, exact private target, and verified native daemon generation when available. Ordinary completions name the ancestral definition instead of copying its map. Dedicated snapshot results and requested structured exports still retain their full data. Native delta baselines remain native; a Pi snapshot ID cannot restore them.
10
10
 
@@ -28,6 +28,8 @@ When upstream ships a new `agent-browser` or the inventory changes:
28
28
 
29
29
  ## Current browser contract
30
30
 
31
+ Native text and artifact ownership: the registered artifact owner covers empty-row screenshot alignment, nonscreenshot placeholders and bail truncation; the upgrade owner covers one-copy failed explicit text. The text/presentation owners cover cookie/storage format redaction across content, details, complete spills and exports, effective raw/stdin batch intent and native JSON all-entry maps. Real-native cases exercise screenshot paths, explicit/shorthand sensitive-key storage reads, empty-name cookies and CLI true/false helper continuity over opposite env/config defaults. Debug false creates no native log with clean or opposite defaults; later calls retain native inheritance. Native unescaped multiline all-entry/raw-batch text retains the documented heuristic ceiling; structured JSON provides the unambiguous storage boundary.
32
+
31
33
  The compact direct/code/loader surface replaces public multimode inputs while retaining native commands and advanced outcomes. This section describes the implementation contract; dated evidence below does not qualify new Pi 1.0 code.
32
34
 
33
35
  | Area | Contract and verification target |
@@ -82,7 +84,7 @@ Current summary:
82
84
  | RQ-0142 | Persistent `agent_browser_code` uses the retained permissioned child and the same native executor as direct calls. Browser/auth state persists; JS state resets. Selected JSON/images, handled-failure receipts/counts, bounded IPC/deadline, shared whole-cell ordering, and ordered Pi transition replay replace isolated finally-close execution. The optional host `beforeExecute` callback retains outer call identity and serial dispatch. Strict old lease readers exist only for upgrade cleanup. | [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#agent_browser_code), [`ARCHITECTURE.md`](ARCHITECTURE.md#persistent-browser-code), `test/agent-browser.script.test.ts`, `test/agent-browser.tool-surface.test.ts`, `test/agent-browser.execution-lock.test.ts`, `test/agent-browser.execution-order.test.ts` |
83
85
  | RQ-0143 | Browser-backed calls require a stable upstream version at or above the configured 0.35.0 floor, report minimum/observed version mismatch before launch, and keep stateless/local diagnostics available; the recommended target and minimum are shared by runtime, docs verification, and package contents. | [`scripts/agent-browser-target.mjs`](../scripts/agent-browser-target.mjs), [`docs/COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#upstream-0360-rebaseline), `test/agent-browser.upstream-version.test.ts` |
84
86
  | RQ-0144 | Managed restore keys are scoped to both checkout generation and Pi transcript so upstream 0.33.2's newest-file-wins loading cannot let concurrent chats clobber or inherit cookies/storage. Fresh rotations, reload, restart, and `/resume` of one transcript retain continuity; after a confirmed inactive daemon, a branch-restored compatible owned session retains its pending reopen through daemon-starting non-page calls and transcript replay, then reopens the complete recorded URL (including its fragment) before current-page operations such as `get url` or `reload`, verifies the actual target and invalidates old refs. This reloads a URL, not forms, JavaScript memory, or history; live wrong-tab recovery never auto-navigates. Passive and explicit upstream configs pass through, while explicit config selection suppresses the wrapper's automatic restore injection. | [`docs/ARCHITECTURE.md`](ARCHITECTURE.md), [`docs/TOOL_CONTRACT.md`](TOOL_CONTRACT.md), `test/agent-browser.managed-session-restore.test.ts`, `test/agent-browser.real-upstream-contract.test.ts` |
85
- | RQ-0145 | Mega-dogfood correctness closes false-positive success paths: URL QA makes matched post-clear page-error evidence non-pass as unverified, counts definitely new errors separately, and waits 150 ms for immediate post-load diagnostics, stale pre-existing artifacts and missing/stale recordings finalized by restart fail verification, aliased destinations (including dangling symlinks, full macOS/Windows Unicode folds, final short/long diff output flags, and same-call `outputPath` aliases) and active recording-path reuse through artifacts, path-bearing HAR stops, long/short or reordered explicit download waits, interspersed-global command forms, Electron cleanup output, or normalized output writes is rejected through an atomic process-wide, transcript-backed namespace/session reservation index independent of bounded manifest eviction; cross-branch close tombstones survive reload and each direct or ordered nested close retires exact identity at that lifecycle point while terminal closes clear attached/ref/page/route state, later lifecycle-proven browser launches—including failed post-close rows and `record stop`—can reactivate/rebuild it without stale pre-close `about:blank` recovery while bounded launch evidence survives failed-step presentation and transcript replay, explicitly non-launching diagnostics preserve the close, missing evidence stays conservatively active even on the first managed call, every successful direct/nested close clears wrapper trace/profiler ownership before later successful rows can rebuild it, and namespace-scoped `close --all` clears all matching managed/attached/page/ref/route/trace/recording ownership, only the newest pending recording path remains authoritative even across same-timestamp restart rows, current recording transitions replay directly without artifact-manifest compatibility inference, definitive direct/batch no-recording stop failures retire stale state at their ordered step, recording starts after nested close are rejected, pending rows coalesce into terminal batch outcomes without retaining an intermediate close-abandoned duplicate after a later saved stop, and any later same-session failure retains exact cleanup actions, same-page getter refs are refreshed, locator select resolves exact current native controls, no-movement scroll fails honestly, and existing owned cleanup preserves failure evidence. | [`docs/TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), [`docs/COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md), `test/agent-browser.extension-input-modes.test.ts`, `test/agent-browser.extension-ref-guards.test.ts`, `test/agent-browser.presentation.test.ts`, `test/agent-browser.presentation-artifacts-batch.test.ts`, `test/agent-browser.recording-reservations.test.ts`, `test/agent-browser.extension-validation.test.ts`, `test/agent-browser.script.test.ts` |
87
+ | RQ-0145 | Mega-dogfood correctness closes false-positive success paths: URL QA makes matched post-clear page-error evidence non-pass as unverified, counts definitely new errors separately, and waits 150 ms for immediate post-load diagnostics, stale pre-existing artifacts and missing/stale recordings finalized by restart fail verification, aliased destinations (including dangling symlinks, full macOS/Windows Unicode folds, final short/long diff output flags, and same-call `outputPath` aliases) and active recording-path reuse through artifacts, path-bearing HAR stops, long/short or reordered explicit download waits, interspersed-global command forms, Electron cleanup output, or normalized output writes is rejected through an atomic process-wide, transcript-backed namespace/session reservation index independent of bounded manifest eviction; cross-branch close tombstones survive reload and each direct or ordered nested close retires exact identity at that lifecycle point while terminal closes clear attached/ref/page/route state, later lifecycle-proven browser launches—including failed post-close rows and `record stop`—can reactivate/rebuild it without stale pre-close `about:blank` recovery while bounded launch evidence survives failed-step presentation and transcript replay, explicitly non-launching diagnostics preserve the close, missing evidence stays conservatively active even on the first managed call, every successful direct/nested close clears wrapper trace/profiler ownership before later successful rows can rebuild it, and namespace-scoped `close --all` clears all matching managed/attached/page/ref/route/trace/recording ownership, only the newest pending recording path remains authoritative even across same-timestamp restart rows, current recording transitions replay directly without artifact-manifest compatibility inference, definitive direct/batch no-recording stop failures retire stale state at their ordered step, recording starts after nested close are rejected, rejected or pending restarts keep the active take's reservation and stop action unless a terminal previous-take receipt or native `restarted: true` acknowledges replacement, pending rows coalesce into terminal batch outcomes without retaining an intermediate close-abandoned duplicate after a later saved stop, and any later same-session failure retains exact cleanup actions, same-page getter refs are refreshed, locator select resolves exact current native controls, no-movement scroll fails honestly, and existing owned cleanup preserves failure evidence. | [`docs/TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), [`docs/COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md), `test/agent-browser.extension-input-modes.test.ts`, `test/agent-browser.extension-ref-guards.test.ts`, `test/agent-browser.presentation.test.ts`, `test/agent-browser.presentation-artifacts-batch.test.ts`, `test/agent-browser.recording-reservations.test.ts`, `test/agent-browser.extension-validation.test.ts`, `test/agent-browser.script.test.ts` |
86
88
  | RQ-0146 | Upstream `agent-browser 0.34.0` rebaseline adds sticky `--pin-tab` / `--no-pin-tab` (`AGENT_BROWSER_PIN_TAB`) for shared CDP/auto-connect sessions, CDP target ids as tab refs, and `tab_gone` recovery data (`data.targetId`, optional `data.lastUrl`). The wrapper parses those flags as optional global booleans rather than launch-scoped, classifies `tab_gone` as `failureCategory: "tab-gone"` with `list-tabs-after-tab-gone` / `open-tab-after-tab-gone`, and shows `targetId` in `tab list` presentation. | [`docs/COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#upstream-0340-rebaseline), [`docs/TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), `test/agent-browser.results.test.ts`, `test/agent-browser.runtime.test.ts` |
87
89
  | RQ-0147 | Local Chrome composes stock `--no-startup-window` with native launch defaults while headless remains the default; URL-less open uses native lazy URL reads across direct/batch/code paths. Existing root/owned daemon probes and explicit-local session info gate bootstrap, without tab cleanup or a launcher/state journal. See `test/agent-browser.stock-startup.test.ts` for stock browser PID/page/storage checks. Native follow-ups preserve profiled and other launch-configured browser pages by omitting wrapper compatibility launch arguments while the daemon is active; daemon inspection reapplies the retained compatibility user agent only when relaunch is required. Caller `--args` retain their values after the startup default and native precedence; config, file access, and explicit local navigation remain native-owned, and cancellation wins over policy validation before spawn. | [`docs/ARCHITECTURE.md`](ARCHITECTURE.md), [`docs/TOOL_CONTRACT.md`](TOOL_CONTRACT.md), `test/agent-browser.runtime.test.ts`, `test/agent-browser.process.test.ts`, `test/agent-browser.real-upstream-contract.test.ts` |
88
90
  | RQ-0148 | Upstream 0.35.0 adds Linux Chromium private CA trust and the `protected-vercel-deployments` skill. The wrapper parses CA set/clear globals, requires fresh managed launches, suppresses automatic managed restore for CA-enabled sessions, passes caller certificate paths upstream unchanged, and uses 0.35.0 as its runtime floor. | [`docs/COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#upstream-0350-rebaseline), [`docs/TOOL_CONTRACT.md`](TOOL_CONTRACT.md), `test/agent-browser.runtime.test.ts`, `test/agent-browser.managed-session-restore.test.ts`, `test/agent-browser.page-target-validation.test.ts` |
@@ -96,6 +98,8 @@ Browser journal storage (RQ-0153) uses one canonical small event/reducer, once-s
96
98
 
97
99
  QA truthfulness (#121, RQ-0145) is covered by the registered-tool controls in `test/agent-browser.extension-input-modes.test.ts` and the strict `contract suite matches QA non-pass after same-URL error-buffer rollover` case in `test/agent-browser.real-upstream-contract.test.ts`. The real fixture fills the native error FIFO with identical rows, then verifies that a new document actually throws again at the same URL and that QA is always non-pass. Matched rows fail as an unverified check, not an application-error assertion. Empty/cleared and explicit `checkErrors: false` controls preserve valid passes; the wrapper does not fix upstream `errors --clear` or accept a warning-only pass as resolution.
98
100
 
101
+ Explicit native text preserves standalone and compound/raw-or-stdin batch output without changing caller input. `test/agent-browser.text-output.test.ts` covers opaque JSON/confirmation-like page strings, complete whitespace-preserving stdout and redacted subprocess/presentation spills, text-only output export success/failure notices, nonzero failure and strict malformed JSON; the `real native explicit text` case in `test/agent-browser.real-upstream-contract.test.ts` proves native URL reads, both diff orders, row boolean operands and last-wins outer selection. The same native case protects empty stdin-row skipping, later successful rows, Unicode results, bail behavior and original row positions in text errors; malformed shapes still fail before dispatch. Failed-exit stderr is primary while zero-exit malformed JSON remains strict, covered by the registered `failed-json`/`strict-json` cases. Text contains no trusted row, confirmation, lifecycle, page-target or artifact receipts; use JSON for those fields. See [the output contract](TOOL_CONTRACT.md#wrapper-json).
102
+
99
103
  Browser evidence (RQ-0152) is covered by `test/agent-browser.browser-evidence.test.ts`, `test/agent-browser.read-confirmation.test.ts`, `test/agent-browser.recording-receipts.test.ts` and `test/agent-browser.recording-recovery.test.ts`, alongside existing presentation, reservation/replay and lifecycle checks. They cover cold/shared/unknown/invalid/timeout reads, native-default and shared confirmation routing, capability absence and script identity boundaries, separate daemon/browser/Pi identity, sparse/legacy capture metrics, native failure retention, stale/same-path mismatches, one-query stop recovery, error exports and artifact aliases. Full live identity, receipt lookup and browser-independent native read/confirm handling require companion upstream fixes; missing fields remain unknown, and native receipts do not survive daemon exit. These source checks do not replace paired native/Pi runtime verification.
100
104
 
101
105
  Contributor fixes #133/#152 remove unused prompt suffix entries without changing runtime guidance and diagnose bare `--no-sandbox` only in the command slot or navigation option positions. Native `--args` values and literal operands remain intact; batch checks use raw effective rows without treating row-local `--args` as a launch setting. `test/agent-browser.chromium-args.test.ts` covers pre-dispatch rejection, literal/flag-value controls, inspection, raw/stdin batch precedence and dispatcher outcomes. Existing prompt/grammar checks remain; source checks do not replace native-product gates.
@@ -118,6 +122,8 @@ Overlay recovery (RQ-0073, #147) recommends only `inspect-overlay-state` after a
118
122
 
119
123
  Lightpanda support preserves native command access rather than promising Chrome parity. Registered-tool image controls cover native metadata in direct/JSON, batch and selected code attachments plus standalone browser-brand evidence when legacy/attached native output omits or mislabels the engine. Verified image bytes remain unchanged and coordinate claims stay unknown even when dimensions match synthetic browser geometry. Root-default controls cover CLI/env/native-config precedence and untouched caller settings; engine planner controls preserve fresh/new/explicit launches. See [engine limits](COMMAND_REFERENCE.md#lightpanda-engine). Release evidence below remains separate from these focused regressions.
120
124
 
125
+ Confirmation-policy continuity uses existing canonical session page events, resolved before helpers. The focused native nextAction case proves exact denial, a useful confirmed audit followed immediately by title/body reads across resume, bare getter/PID continuity, and denied recording-restart sentinel/original-take preservation; the shared-defaults owner covers selected-string/empty/unset precedence, code/advanced routing, ordered pending-batch targets/replay with a gated URL helper, failed replacement cleanup/recovery, off-branch cleanup, close and identity isolation. Strict native guarded-control markers reuse the same transcript reducer with compact command/action provenance, not command replay; exact decisions avoid helper-slot replacement without claiming native ID validation or browser independence. Read-confirmation tests cover forged/foreign/first-nondecision controls, raw-argv-over-stdin decisions, later unknown-page checks, ordered confirmed batch effects, truthful requested/early-helper/post-command-helper pending copy, and completed click/tab/failed-inner/close reconciliation through real code intent/finish replay, cold-resume covers pending helper exposure/replay, and Electron host tests cover ambient/retained policy. Definite no-admission attempts restore prior policy while interrupted work retains intent. Pending close does not retire ownership or restore state. Native policy changes still restart normally; pending commands remain native-owned. Read-confirmation regressions also cover ordered confirmed snapshots, later pending/failed/invalidation rows, one-use freshness consumption through direct/code/batch resume, semantic role click/check/fill and unique role/label select after snapshot approval with unchanged payloads, absent/ambiguous select targets and drift, warm/no-resume direct and semantic select success on unchanged path/hash routes for caller-owned and freshly established wrapper-owned managed sessions versus refusal after a live-observed complete-URL change, including hash-only routes, without a gated-snapshot loop or changing ordinary fragment-insensitive ref/tab comparisons, plus live/resumed attachment retirement after confirmed close. Registered confirmation tests also cover direct/batch tab transport metadata, exact approval/denial, visible conditional continuation, malformed/mismatched extras and page/eval/HTTP provenance rejection; actual SDK/native proof uses the real authenticated-tab transport with a dummy loopback token. Presentation tests distinguish direct/nested pending snapshot controls from complete empty snapshots and preserve redacted recovery actions and explicit JSON. Click-dispatch owner tests and local native SDK proof cover timer cancellation, pending-safe marker/listener expiry without an extra helper, and native ref/XPath click dispatch when `evaluate` guards optional diagnostic probes. The patched recording-restart/navigation subcase has a separate test-only opt-in; stock upstream still runs the remaining confirmation checks. See [real upstream qualification](RELEASE.md#real-upstream-contract-validation).
126
+
121
127
  Shared native defaults are covered by `test/agent-browser.shared-defaults.test.ts`: environment/file/argv precedence, caller-owned reuse across independent Pi contexts, quit ownership, native helper idle settings, unchanged implicit cleanup, and an opt-in real local-profile fixture proving shared state and code follows the selected ordinary browser rather than a disposable isolated session. Code tests cover the fresh child language boundary and persistent browser behavior; the local Electron handoff regression in `test/agent-browser.extension-electron-lifecycle.test.ts` keeps generated launch ownership despite a native session environment default; native config validation uses upstream rather than a second schema. See [shared browser defaults](../README.md#shared-browser-defaults). These tests do not promise permanent third-party authentication.
122
128
 
123
129
  Browser correctness regressions cover credential-bearing filtered network details/exports, native generated-Blob downloads with actual saved bytes, accessible-name click target identity and abstention, smooth CSS-container movement, and parseable early-result JSON. Native fixture coverage complements deterministic dispatch/cleanup and output-shape checks; release qualification remains separate.
@@ -155,7 +161,7 @@ Re-run the gates below before each release. Dated outcomes in this table are his
155
161
  | Configured-source lifecycle | `npm run verify -- lifecycle` (`scripts/verify-lifecycle.mjs`) drives `/reload`, closes and relaunches Pi with the same exact `--session-id`, checks the JSONL session header id, session continuity, slash-command sentinel tokens (`v1` before reload and `v2` after full relaunch), persisted spill reachability, and native returned failure status and JSON preservation for a QA reclassification with a fake upstream on `PATH`. Default Pi model is `zai/glm-5.2`; default per-step wait is **180000 ms** (`DEFAULT_TIMEOUT_MS`); override model with `--model <id>` and waits with `--timeout-ms <ms>`. Passthrough flags in [`scripts/project.mjs`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/project.mjs): `--keep-artifacts`, `--model`, `--verbose`, and `--timeout-ms` plus a value (for example `npm run verify -- lifecycle --model openai-codex/gpt-5.5:minimal --keep-artifacts --verbose --timeout-ms 600000`). | **Pi 0.84.4 on 2026-09-01:** pass; `/reload`, exact-session relaunch, managed continuity, persisted spill reachability, and Pi failure status all verified. |
156
162
  | Quick isolated Pi smoke | `pi --approve --no-extensions --no-skills -e .` from trusted repo root; checkout-native browser tools only. | **Earlier 0.36.0 / package 0.6.2 / Pi 0.84.4:** isolated checkout tmux smoke passed on 2026-09-01; native `agent_browser` completed version, localhost open, WebMCP list/invoke, visible-text verification, and close. |
157
163
 
158
- Runtime floor note: package metadata requires Node 24.21.0 or newer, keeps optional Pi core package peer ranges wildcard per installed Pi package docs, pins direct Pi validation dependencies to 1.0.0, and makes `pi --version` below 1.0.0 a setup failure through `pi-agent-browser-doctor` / `npm run doctor`. The Pi floor follows the native tool-history/prompt APIs and includes no older-host compatibility shim.
164
+ Runtime floor note: package metadata requires Node 24.21.0 or newer, keeps optional Pi core package peer ranges wildcard per installed Pi package docs, keeps direct Pi development dependencies locked as reproducible snapshots while qualification resolves the latest stable official Pi and maintained fork main independently, and makes `pi --version` below 1.0.0 a setup failure through `pi-agent-browser-doctor` / `npm run doctor`. The Pi floor follows the native tool-history/prompt APIs and includes no older-host compatibility shim.
159
165
 
160
166
  ## Baseline checklist by inventory section
161
167
 
@@ -166,7 +172,7 @@ Runtime floor note: package metadata requires Node 24.21.0 or newer, keeps optio
166
172
  | Sessions, state, tabs, frames, dialogs, and windows | 28 canonical tokens from baseline section `state-tabs-frames-dialogs`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#session-state-frames-dialogs-windows-and-inspection-commands). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#session-state-frames-dialogs-windows-and-inspection-commands), stateful workflow notes, [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details). | Stateful summaries redact credentials while preserving restore identifiers, every session/state list row, and ordinary `cookies set --curl` file operands in invocation echoes and failed batch diagnostics; explicit targets and paths pass through. Runtime retains state artifact verification, sessionless local command planning, automatic managed restore, native-CDP-ID tab recovery across duplicate URLs and transcript replay, reached failed-batch target invalidation/live verification, active-target refresh after tab close, and close alias cleanup. | Extension tab/ref tests, real-upstream stable-id/label tab lifecycle, runtime session/resume tests, presentation redaction tests, lifecycle harness. | Supported. External profile/auth state remains operator-owned. |
167
173
  | Network, storage, artifacts, diagnostics, and performance | Canonical tokens from baseline section `network-storage-artifacts-diagnostics`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#page-state-finding-mouse-settings-network-and-storage). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#page-state-finding-mouse-settings-network-and-storage), diagnostic sections, [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details). | Thin passthrough plus native-owned click/download execution for all downloads (including loopback, Blob exports, and redirects), compact diagnostics with identical credential redaction for page/origin/URL-filtered details and exports, route-mock warnings, useful-but-redacted storage output, stream idempotency normalization, FPS/cursor/contact-sheet-aware recording destinations/pinning and contact-sheet image verification, truthful ref-refresh and missing-ffmpeg warnings, positive WebMCP navigation hints and catalog updates, sensitive-data redaction, timeout bounds, and cleanup-pair guidance. | Fake non-core matrix and safe real-upstream coverage for network/HAR, diff, trace/profiler, console/errors/highlight, stream, vitals, React missing-renderer, native FPS recording path/page checks and WebMCP navigation hints. | Supported. Environment-sensitive operations need suitable local/browser state. |
168
174
  | Batch, auth, confirmations, setup, dashboard, devices, and AI commands | Canonical tokens from baseline section `batch-auth-setup-ai`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#batch-auth-confirmations-sessions-chat-dashboard-devices-and-setup). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#batch-auth-confirmations-sessions-chat-dashboard-devices-and-setup), README security notes, release docs. | Native-tool batch stdin, generated QA/lookup batch plans, auth/confirmation redaction, sessionless local auth/setup/dashboard/doctor/plugin planning, upgrade-only native text normalization with error precedence and explicit-JSON results, plugin list/show JSON envelope normalization, bare-`mcp` validation with `mcp --help` preserved, timeout/cleanup guidance. | Parser/runtime plugin and MCP unit coverage; fake-upstream plugin list/show and MCP help/blocking coverage; registered upgrade text/success/nonzero/timeout/abort/JSON controls in `test/agent-browser.upgrade-output.test.ts`; real-upstream plugin list shape probe; structured input-mode tests. | Supported. Interactive side-effecting setup/auth/chat remains upstream-owned. `plugin` is local/sessionless; `mcp` is external-client-only except help; `auth login --credential-provider` resolves credentials via a plugin; `install --with-deps` failures remain upstream-owned. |
169
- | Global flags, config, providers, policy, and environment | Canonical tokens from baseline section `options-and-env`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#important-global-flags-config-and-environment). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#important-global-flags-config-and-environment), README provider/setup notes, [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#sessionmode), architecture/runtime docs. | Runtime preserves caller-requested JSON for early filters, scroll results, and preparation failures without output-file prose (help/version remains native text), and handles command discovery, value-flag prevalidation, launch-scoped flags, redacted echoes, fresh-session recovery hints, explicit sessions, provider/device launch-scoping, parent env forwarding with consistent managed idle-timeout/socket/operation-timeout overrides on top-level and helper subprocesses, subprocess completion, and package-owned Pi-scoped config for optional companion features. | Runtime tests for flags/planning/redaction/session behavior; process/ref tests for env consistency and stdio-linger completion; config/web-search/CLI tests; fake provider/specialized-skill matrix; package doctor. | Supported. Provider clouds, iOS/Appium, proxies, profiles, and credentials require external setup. |
175
+ | Global flags, config, providers, policy, and environment | Canonical tokens from baseline section `options-and-env`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#important-global-flags-config-and-environment). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#important-global-flags-config-and-environment), README provider/setup notes, [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#sessionmode), architecture/runtime docs. | Runtime preserves caller-requested JSON for early filters, scroll results, and preparation failures without output-file prose (help/version remains native text), and handles command discovery, value-flag prevalidation, launch-scoped flags, redacted echoes, fresh-session recovery hints, explicit sessions, provider/device launch-scoping, parent env forwarding with consistent managed idle-timeout/socket/operation-timeout overrides and per-call explicit policy/confirmation context on top-level and helper subprocesses, subprocess completion, and package-owned Pi-scoped config for optional companion features. | Runtime tests for flags/planning/redaction/session behavior; process/ref tests for env consistency and stdio-linger completion; config/web-search/CLI tests; fake provider/specialized-skill matrix; package doctor. | Supported. Provider clouds, iOS/Appium, proxies, profiles, and credentials require external setup. |
170
176
 
171
177
  ## Follow-up decision after closure
172
178
 
@@ -168,15 +168,21 @@ Use exact labels from the latest snapshot. Return to the model when a fresh obse
168
168
 
169
169
  `screenshot --if-changed` and `--threshold <0-1>` preserve native `changed`, `revision`, `pixelChangeRatio`, and `threshold`. An unchanged response has no path, artifact, or image attachment—even if the caller requested an existing file. It succeeds as an observation, not proof that an evidence file was saved.
170
170
 
171
- `record start` / `restart` accept `--cursor`, `--contact-sheet`, and `--contact-sheet-threshold <n>`. Reported `contactSheetPath` values become image artifacts: pending during capture, checked on disk after stop, and attached when verified. The contact-sheet destination shares the active video's reservation and transcript lifecycle; same-call/batch artifact and `outputPath` aliases are rejected before dispatch. A successful direct `record restart` retains the prior take's known contact-sheet path from the existing reservation, checks it on disk, and labels it unverified because native restart omits terminal sheet evidence. Use `record stop` then a new start when verified completion of both artifacts is required. Video receipt handling remains separate. Native `--input-mode <mode>` is a sticky, live-changeable session setting, not a launch flag; per-action pointer options pass through. See the [command reference](COMMAND_REFERENCE.md#upstream-0381-rebaseline).
171
+ `record start` / `restart` accept `--cursor`, `--contact-sheet`, and `--contact-sheet-threshold <n>`. Reported `contactSheetPath` values become image artifacts: pending during capture, checked on disk after stop, and attached when verified. The contact-sheet destination shares the active video's reservation and transcript lifecycle; same-call/batch artifact and `outputPath` aliases are rejected before dispatch. A successful direct `record restart` retains the prior take's known contact-sheet path from the existing reservation, checks it on disk, and labels it unverified because native restart omits terminal sheet evidence. Use `record stop` then a new start when verified completion of both artifacts is required. Video receipt handling remains separate. Without a terminal `previousRecording` receipt, only native `restarted: true` retires the prior take; rejected or pending restarts preserve its reservation and exact stop action. Native `--input-mode <mode>` is a sticky, live-changeable session setting, not a launch flag; per-action pointer options pass through. See the [command reference](COMMAND_REFERENCE.md#upstream-0381-rebaseline).
172
172
 
173
173
  <a id="wrapper-json"></a>
174
174
 
175
175
  ## Wrapper `--json`
176
176
 
177
+ Native text cookie reads redact `name=value`, including empty names; storage reads redact `key: value`, including empty and colon-containing keys, with the existing benign-value policy. A standalone get-by-key (`storage local|session get <key>` or native shorthand `storage local|session <key>`) redacts the entire value, including embedded newlines, when that policy requires it, even if invocation display redaction changes the supplied key. Content, details, spills and exports use the same redacted text. Effective batch argv takes precedence over stdin when selecting these format heuristics; text never establishes per-row provenance. Native all-entry storage and raw-batch output do not escape multiline values: a secret continuation can look identical to a separate benign entry or ordinary page output. Text redaction cannot resolve that ambiguity; use structured JSON for an unambiguous sensitive-storage boundary. Native JSON all-entry maps retain names and benign values while redacting other values.
178
+
177
179
  When explicitly requested, visible JSON remains parseable for early preparation results too: snapshot/network filters, scroll results, and preparation failures use the same JSON envelope as ordinary execution. Diagnostic metadata remains in `details`; `outputPath` notices do not append prose to JSON. Help/version stays native text.
178
180
 
179
- The extension always plans normal browser commands with `--json` prepended in `effectiveArgs` so upstream returns structured JSON for presentation and `details`. Omit `--json` in caller `args` for ordinary prose. Include it when you need the visible tool text as a parseable JSON envelope; structured `details` remain available in both modes. Plain-text inspection (`--help`, `--version`) keeps its own output shape. Read-only skills and local/setup commands such as auth/profile/setup, `session list`, and syntactically local state lifecycle operations skip implicit session injection as documented under `sessionMode`. Upstream session/state rows and targets remain visible, and state/config/path operations pass through unchanged.
181
+ The extension prepends `--json` to normal browser commands when the caller does not select an output mode, so upstream returns structured JSON for presentation and `details`. Omit `--json` in caller `args` for ordinary prose. Include it when you need the visible tool text as a parseable JSON envelope; structured `details` remain available in both modes. Plain-text inspection (`--help`, `--version`) keeps its own output shape. Read-only skills and local/setup commands such as auth/profile/setup, `session list`, and syntactically local state lifecycle operations skip implicit session injection as documented under `sessionMode`. Upstream session/state rows and targets remain visible, and state/config/path operations pass through unchanged.
182
+
183
+ Explicit `--json false` selects native text, including native batch output. Optional booleans use upstream's last-occurrence-wins semantics; `--json` and `--json true` select JSON. Caller argv/stdin, config, and environment remain unchanged. Native text is retained as a redacted string, including leading/trailing whitespace and line breaks, even when it resembles JSON or a confirmation banner. Inline data, complete spills and output exports preserve that whitespace. Success means the process completed successfully; nonzero exit, stderr errors, timeout, cancellation, and spawn failure remain failures. Text does not supply trusted batch rows, pending confirmations, lifecycle, page-target or artifact receipts. URL-shaped page content cannot replace the session target; only structured native observations can establish it. Saved-output notices stay prose and never parse native text as JSON, including when an export fails. Native confirmation enforcement and visible confirm/deny instructions remain upstream-owned; the wrapper never auto-confirms. Use JSON mode when structured evidence is required. Helpers that need structured state continue requesting and strictly parsing JSON. Explicit `--action-policy`, `--confirm-actions`, `--debug` and `--no-auto-dialog` selections reach those helpers within the current call, including CLI boolean `false` over true config/environment defaults; the established `--confirm-actions` setting is retained in the existing session lifecycle state for same-identity helpers and follow-ups, including exact confirm/deny actions, code/advanced calls and resume. First launch and new explicit `--confirm-actions` / `--config` selections use native argv → environment → config precedence. Bare follow-ups retain selected strings, including explicit `""`, over ambient environment/discovered-config edits. Native unset is not pinned and continues native default resolution. Native compound confirmations remain pending until every required action is decided; pending navigation never becomes an observed page target, including native batch rows. Completed non-read confirmation results establish their resulting page target before ordinary follow-ups. Successful close retires the setting; replacement cleanup uses the old identity's setting and preserves continuity if close fails; Electron launch and host helpers use the same selected setting. Definitely unestablished attempts (wrapper rejection, native CLI parse/config failure or local inspection) restore the prior setting when no earlier browser helper could have established the request; unknown/interrupted dispatch retains selected intent. Other settings remain call-scoped and must stay consistent to avoid native daemon replacement.
184
+
185
+ Empty stdin batch rows (`[]`) pass through and are skipped by native execution; `[""]` remains a failing command. Native bail behavior and later reached rows remain intact. When an upstream process exits nonzero with stderr instead of an envelope, stderr is the primary error and `failureCategory` is `upstream-error`; `details.parseError` retains secondary diagnostics. Zero-exit malformed machine output still fails strict JSON parsing.
180
186
 
181
187
  ## Experimental WebMCP
182
188
 
@@ -239,7 +245,7 @@ URL-less `open` is executed as native `get url`, including effective raw/stdin b
239
245
  - For non-core families, pass current upstream commands through the native tool directly: network requests, network route <url>, network har start/stop [path], diff snapshot, diff screenshot --baseline <file>, diff url <u1> <u2>, trace start, trace stop [path], profiler start, profiler stop [path], record start <path>, record stop, console/errors [--clear], highlight <selector>, inspect, clipboard read, clipboard write <text>, clipboard copy/paste, stream enable/disable/status, dashboard start/stop, device list for iOS simulator inventory, and chat <message>. For compact network requests output, prefer details.nextActions for request detail, route-mock diagnostics, actionable failed-request agent_browser_network_source, filtering, clearing the aggregate buffer before repro, or HAR capture follow-ups instead of guessing request-id syntax. Artifact-producing commands report details.artifacts and verification state; long-running starts such as stream, dashboard, trace/profiler, and record should be paired with the matching stop/disable command when the task is done; stream enable already-enabled outcomes are treated as idempotent success with status/disable follow-ups.
240
246
  - For Electron desktop apps, enable agent_browser_electron through agent_browser_tools for wrapper-owned discovery, isolated launch, status, compact probe, and cleanup: list first, treat likely-sensitive annotations as hints rather than enforcement, launch with the default snapshot handoff unless handoff: "tabs" is the safer diagnostic starting point, use agent_browser_electron probe or snapshot -i/agent_browser_qa attached:true for current-session state, and always cleanup the returned launchId when done. agent_browser_electron launch uses an isolated temporary profile; it does not reuse the app's normal signed-in profile or attach to an already-running authenticated app. For signed-in local app state, host-launch the normal app with --remote-debugging-port when appropriate, then use raw args connect <port|url>; after connect, run get url to verify the active target before page-content reads, inspect tab list, select the stable tab id such as tab t2, verify it again with get url, then run a condition wait or snapshot -i before using refs. close commands (`close`, `quit`, or `exit`) only close the browser/CDP session; leave manually launched app shutdown, profile cleanup, and explicit artifacts to the host owner.
241
247
  - For provider or specialized app workflows, load version-matched upstream guidance with skills get agentcore|electron|slack|dogfood|vercel-sandbox|derive-client through the native tool; add --full when you need references/templates, and use skills get --all only for broad skill audits. Use derive-client when recording HAR traffic to generate a standalone API client; prefer network har start (text bodies by default) or network har start --content all|none before multi-step capture. For accessibility audits use a11y or a11y --tags wcag2a,wcag2aa (CDP browsers only). Hosted sandbox workflows should use upstream @agent-browser/sandbox helpers outside this wrapper. Provider launches such as -p ios, --provider browserbase/kernel/browseruse/browserless/agentcore, and iOS --device are upstream-owned setup paths; use sessionMode fresh when switching providers and expect external credentials or local Appium/Xcode setup to be required.
242
- - For dialogs and frames, use dialog status/accept/dismiss and frame <selector|main> through native args; dialog commands and eval snippets that look like alert/confirm/prompt/dialog triggers are shorter-bounded than normal browser calls, and timed-out dialog-like interactions may add inspect-dialog-after-timeout, dismiss-dialog-after-timeout, or recover-fresh-session-after-dialog-timeout nextActions. When --confirm-actions produces a pending confirmation, use details.nextActions or exact confirm <id> / deny <id> calls instead of inventing ids.
248
+ - For dialogs and frames, use dialog status/accept/dismiss and frame <selector|main> through native args; dialog commands and eval snippets that look like alert/confirm/prompt/dialog triggers are shorter-bounded than normal browser calls, and timed-out dialog-like interactions may add inspect-dialog-after-timeout, dismiss-dialog-after-timeout, or recover-fresh-session-after-dialog-timeout nextActions. Set --confirm-actions on the session's first command; selected strings, including an explicit empty string, follow same-identity helpers and resumed follow-ups until successful close. Native unset keeps ordinary native defaults. Changing a running daemon's policy can discard pending confirmations. When it produces a pending confirmation, including a cold-reopen helper, follow the exact details.nextActions or confirm <id> / deny <id> calls without repeating the policy or inventing ids. Matching native guarded controls avoid page helpers that could replace the pending action; this is not native ID-validation proof or browser independence. In native batch, only a matching first effective decision gets that helper suppression; later page/ref checks remain. Nested decisions remain pending; failed inner actions fail, and completed browser actions reconcile their page state. A pending tab_new transport may show a validated after_confirmation open request and conditional guidance: follow it in the same session and new tab only after successful tab approval, never after denial. The extension does not execute that continuation. Confirmed full snapshots install returned refs; a final confirmed capture supplies the next same-identity call's freshness sample once, including semantic role click/check/fill and unique accessible select resolution, with target/membership/invalidation checks intact. On this one use, live verification compares the complete capture URL, including fragments, and rejects refs when it changes—including wrapper-owned managed sessions and warm sessions without resume. Ordinary ref/tab URL comparisons remain fragment-insensitive. Pending snapshots display confirmation required, not an empty page. Intervening calls or later rows consume it; DOM-only renames on the same page after that sample are not resampled on this one use.
243
249
  - If a session lands on the wrong page or tab, an interaction changes origin unexpectedly, or an open call returns blocked, blank, or otherwise unexpected results, use tab list / tab <tab-id-or-label> / snapshot -i to recover state before retrying different URLs or fallback strategies. For headed demos, put --headed on the first launch with sessionMode=fresh and verify with screenshot/tab/get-url evidence because tool success cannot prove the OS window is visible to the user. For desktop readiness, prefer real conditions first: wait --text, wait --url, wait --fn, wait --load <state>, wait --download, or agent_browser_qa attached:true; for disappearance checks, use wait --fn predicates instead of stale upstream-help examples like wait <selector> --state hidden. Use agent_browser_electron probe/status for wrapper-owned launch health or target mismatch. Fixed waits are a last resort: their duration is positional (wait <ms>, not wait --time <ms>). Use explicit --timeout or top-level timeoutMs for legitimately slow waits, and treat a successful payload like "waited":"timeout" as elapsed time only—verify completion with an observed condition, fresh snapshot, or screenshot.
244
250
  - For feed, timeline, or inbox reading tasks, focus on the main timeline/list region and read the first item there rather than unrelated composer or sidebar content.
245
251
  - For read-only browsing tasks, use read <url> for documentation or other unstructured text without requiring a Chrome page, or read with no URL for rendered active-tab DOM. Prefer the current snapshot, structured ref labels, getters, or scoped eval --stdin when you need interactive structure or targeted page state. Only click into media viewers, detail routes, or new pages when the current view does not contain the needed information.
@@ -247,7 +253,7 @@ URL-less `open` is executed as native `get url`, including effective raw/stdin b
247
253
  - On dashboards with nested scroll containers, verify scroll with a screenshot or fresh snapshot -i; if the viewport did not move, details.data.scrolled may be false/noMovement true and you should prefer scrollintoview <@ref> or target the actual scrollable region with scroll <selector> <dir> [px|percent]. For native selects, use select <selector> <value...> (or agent_browser_action select) instead of clicking option refs; for custom comboboxes, a click/agent_browser_action may only focus the field, so re-snapshot and use keyboard type <text> for focused input, press ArrowDown or press Enter, or visible option refs. Raw type requires both <selector> and <text>.
248
254
  - When using eval --stdin, scope checks and actions to the target element or route whenever possible instead of relying on broad page-wide text heuristics.
249
255
  - When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file, but never reuse a screenshot, download, recording, or other browser artifact destination as outputPath. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. Local file pages and caller-selected output paths are supported when upstream allows them. If get text on a broad CSS selector surfaces details.selectorTextVisibility or selectorTextVisibilityAll, prefer a visible @ref, a more specific selector, or the inspect-visible-text-candidates nextAction over hidden tab content.
250
- - When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. Click-dispatch probes require native get attr readback of a temporary marker on the candidate target; unproven ref or XPath identity leaves the native click unverified, not failed. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If an upstream click failure says the target is covered by another element at the target's click point, use the inspect-overlay-state nextAction to refresh refs and inspect the blocker before deciding whether to retry; do not blindly repeat the blocked click. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.
256
+ - When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. Optional click-dispatch probes are skipped when native confirmation settings guard evaluate, without bypassing native click policy. Otherwise probes require native get attr readback of a temporary marker on the candidate target; unproven ref or XPath identity leaves the native click unverified, not failed. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If an upstream click failure says the target is covered by another element at the target's click point, use the inspect-overlay-state nextAction to refresh refs and inspect the blocker before deciding whether to retry; do not blindly repeat the blocked click. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.
251
257
  - On upstream 0.38+, use snapshot --delta for native full/unchanged/changed revisions; --delta --full resets its baseline. Surviving DOM nodes keep refs, but refresh after navigation or replacement. Wrapper filters use full trees. screenshot --if-changed or --threshold 0.01 can return changed:false with no path or image; use ordinary screenshots when a file is required. --input-mode instant|smooth|human controls sticky pointer behavior; click/drag --human and mouse move --duration/--steps/--human/--seed pass through. record start/restart accept --cursor, --contact-sheet, and --contact-sheet-threshold; stop and verify both video and reported contact sheet. auth login <name> --no-navigate fills a prepared login page only after native origin checks. WebMCP catalog updates are untrusted page data; webmcp list supplies full schemas.
252
258
  - When commands save or spill files (screenshots, downloads, PDFs, traces, recordings, HAR, large snapshot spills), use the user's exact requested paths when given and treat paths as provisional until details.artifactVerification shows every row verified: branch on missingCount, pendingCount, unverifiedCount, per-entry state, and optional limitation before downstream file use or PASS/FAIL reporting.
253
259
  - For evidence-only screenshots, QA captures, or other audit artifacts, save to an explicit path and branch on details.artifactVerification plus details.artifacts before reporting PASS/FAIL; do not require vision review of inline image attachments unless the user asked for visual inspection.
@@ -271,7 +277,7 @@ URL-less `open` is executed as native `get url`, including effective raw/stdin b
271
277
  - exact CLI args passed after `agent-browser`; this is the 1:1 upstream CLI coverage path for the targeted `agent-browser` version
272
278
  - no shell operators
273
279
  - do not include the binary name
274
- - omit `--json` for prose; include it for parseable model-visible JSON (the wrapper injects upstream JSON internally either way)
280
+ - omit `--json` for prose backed by structured upstream JSON; include `--json` or `--json true` for parseable model-visible JSON; `--json false` selects opaque native text (see [Wrapper `--json`](#wrapper-json))
275
281
  - first-call recipe: `open` → `snapshot -i` → `click` / `fill` with current `@eN` refs from that snapshot → `snapshot -i` again after navigation or DOM changes
276
282
 
277
283
  Examples:
@@ -347,7 +353,7 @@ Enable `action` through `agent_browser_tools`, then call the tool with flat fiel
347
353
  | `role`, `name` | Accessible role and optional name; role may replace locator value and must agree if both are given |
348
354
  | `session` | Explicit native session; preserved in compiled commands and recovery |
349
355
 
350
- Locator click/check/fill compiles to native `find`; direct targets compile to their ordinary command. Select uses native `select <selector> <value...>`. Accessible select resolves exactly one current visible combobox/listbox through a fresh snapshot; missing or ambiguous targets fail before action. Use `role: "combobox"` or `"listbox"` plus `name`, or label plus option `values`. No native `find ... select` capability is assumed.
356
+ Locator click/check/fill compiles to native `find`; direct targets compile to their ordinary command. Select uses native `select <selector> <value...>`. Accessible select resolves exactly one current visible combobox/listbox through a fresh snapshot or the eligible one-use confirmed capture; missing or ambiguous targets fail before action. Use `role: "combobox"` or `"listbox"` plus `name`, or label plus option `values`. No native `find ... select` capability is assumed.
351
357
 
352
358
  ```json
353
359
  { "action": "fill", "locator": "label", "value": "Email", "text": "user@example.com" }
@@ -504,14 +510,14 @@ Recommended use:
504
510
 
505
511
  ## Wrapper behavior
506
512
 
507
- Caller `args` should omit `--json`; the wrapper prepends it for normal execution so `details` and presentation stay structured. See [Wrapper `--json`](#wrapper-json).
513
+ Caller `args` may omit `--json` for structured details and prose; explicit `--json false` selects opaque native text. See [Wrapper `--json`](#wrapper-json).
508
514
 
509
515
  The extension should:
510
- - inject `--json`
516
+ - inject `--json` when no caller output mode is selected
511
517
  - invoke `agent-browser` directly, not through a shell
512
- - parse JSON output into tool details
518
+ - parse JSON-mode output into tool details
513
519
  - handle observed JSON result shapes, including the array returned by `batch --json`
514
- - allow plain-text output for native inspection calls and valid sessionless `upgrade` commands; all other commands retain JSON envelope validation
520
+ - allow plain-text output for native inspection calls, valid sessionless `upgrade` commands and explicit `--json false`; JSON-mode browser commands retain strict envelope validation
515
521
  - support those inspection calls unconditionally so the tool contract stays local and predictable
516
522
 
517
523
  <!-- agent-browser-playbook:start inspection -->
@@ -635,7 +641,7 @@ For `batch`, each `batchSteps[]` entry can carry its own `nextActions` for that
635
641
 
636
642
  `pageChangeSummary` is an optional compact summary for mutation-prone and artifact-producing commands. It includes `changeType` (`"navigation"`, `"mutation"`, `"artifact"`, or `"confirmation"`), `observed`, `command`, a readable `summary`, optional `title`/`url`, optional `artifactCount` or `savedFilePath`, and `nextActionIds`. `observed: false` means upstream dispatched a mutation-capable action but the wrapper did not observe an application change; standalone results also append a visible `Action dispatched; application change unverified` warning. The wrapper maintains explicit command/subcommand capability checks through `isPageChangeSummaryCommand` in `extensions/agent-browser/lib/command-taxonomy.ts`: those commands still emit a `mutation`-typed summary when upstream JSON lacks navigation metadata, as long as no stronger signal (artifact, saved path, navigation fields, or pending confirmation) applies. That capability is independent from `invalidatesBatchRefs` and `triggersPostMutationSnapshot`, so artifact summaries like `download` / `screenshot` and guarded-but-non-invalidating `fill` are documented directly in the capability table instead of implied by broad set spreading. Commands outside that set omit `pageChangeSummary` unless the parsed payload shows navigation, a confirmation prompt, saved files, or artifacts—including read-only inspection commands, which normally have no summary unless one of those signals appears. For `batch`, the top-level summary favors artifact rollups when any step produced artifacts; otherwise it synthesizes an observed-or-unverified summary from step evidence. Visible batch output promotes dispatch-only mutation evidence before step details and states that fixed waits are not postconditions. Agents should verify URL/text/state or an external receipt for important mutations before continuing.
637
643
 
638
- `clickDispatch` may appear after a **top-level non-Electron** direct `click` when the wrapper installed a target-specific DOM-event probe, upstream reported success, and the post-click probe found no trusted DOM event reached the resolved target. Target-specific probes cover `xpath=` targets and role-gated `@e…` refs when the latest wrapper-tracked snapshot has role/name metadata; eligible ref roles are `button`, `checkbox`, `menuitem`, `radio`, `switch`, and `tab`, and the role/name must be unique in both the saved snapshot and the live candidates. Before trusting either a ref or XPath candidate, the wrapper sets a per-probe temporary DOM attribute and requires native `get attr <original-selector> <marker>` readback to match it. Missing or mismatched identity, including XPath/frame scope mismatches, cleans up the probe and leaves the native click to run without dispatch verification. This does not implement a new accessible-name algorithm or promise that every ref is observable. Duplicate-name refs pass through without a probe because snapshot order can change and is not proof of target identity. Raw `find … click` locator calls, including compiled `agent_browser_action` clicks that still execute as upstream `find`, are not probed because the wrapper has no concrete element before upstream resolves the locator, and document-level probes can falsely fail frame-scoped clicks. It does **not** take a fresh pre-click snapshot because that could recycle upstream refs before the intended click. The wrapper does **not** replay clicks in-page. On a miss it marks the tool failed, appends `Click dispatch diagnostic: …`, and sets `clickDispatch.status` to `"no-native-event-observed"` with `reason: "native-click-produced-no-target-dom-event"`, `nativeEventCount`, and a redacted `target` descriptor (`kind: "xpath"` plus `selector`, or `kind: "accessible"` plus `refId`, `role`, and redacted `name`). `details.nextActions` gains `inspect-click-dispatch-miss` (`snapshot -i`) and `retry-click-after-dispatch-miss` (same upstream click argv, session-prefixed when applicable). If a local static fixture must be exercised despite this diagnostic, a caller may explicitly run a programmatic activation via `eval --stdin` such as `document.querySelector(...).click()`, but that emits an untrusted scripted event and is only a debugging/workaround path; it must not be used as proof that real user-like clicking works or to bypass prompt stop boundaries. This diagnostic is only for standalone top-level direct click calls; `find` locator clicks and native batch/QA click steps remain upstream-owned behavior.
644
+ `clickDispatch` may appear after a **top-level non-Electron** direct `click` when the wrapper installed a target-specific DOM-event probe, upstream reported success, and the post-click probe found no trusted DOM event reached the resolved target. Target-specific probes cover `xpath=` targets and role-gated `@e…` refs when the latest wrapper-tracked snapshot has role/name metadata; eligible ref roles are `button`, `checkbox`, `menuitem`, `radio`, `switch`, and `tab`, and the role/name must be unique in both the saved snapshot and the live candidates. Before trusting either a ref or XPath candidate, the wrapper sets a per-probe temporary DOM attribute and requires native `get attr <original-selector> <marker>` readback to match it. Missing or mismatched identity, including XPath/frame scope mismatches, cleans up the probe and leaves the native click to run without dispatch verification. Probe listeners and the window marker self-expire after the call's process-timeout budget plus the existing two-second cleanup grace when a pending decision prevents normal cleanup; normal collection cancels the timer. Expiry dispatches no browser helper and missing/expired probes cannot diagnose a dispatch failure. This does not implement a new accessible-name algorithm or promise that every ref is observable. Duplicate-name refs pass through without a probe because snapshot order can change and is not proof of target identity. Raw `find … click` locator calls, including compiled `agent_browser_action` clicks that still execute as upstream `find`, are not probed because the wrapper has no concrete element before upstream resolves the locator, and document-level probes can falsely fail frame-scoped clicks. It does **not** take a fresh pre-click snapshot because that could recycle upstream refs before the intended click. The wrapper does **not** replay clicks in-page. On a miss it marks the tool failed, appends `Click dispatch diagnostic: …`, and sets `clickDispatch.status` to `"no-native-event-observed"` with `reason: "native-click-produced-no-target-dom-event"`, `nativeEventCount`, and a redacted `target` descriptor (`kind: "xpath"` plus `selector`, or `kind: "accessible"` plus `refId`, `role`, and redacted `name`). `details.nextActions` gains `inspect-click-dispatch-miss` (`snapshot -i`) and `retry-click-after-dispatch-miss` (same upstream click argv, session-prefixed when applicable). If a local static fixture must be exercised despite this diagnostic, a caller may explicitly run a programmatic activation via `eval --stdin` such as `document.querySelector(...).click()`, but that emits an untrusted scripted event and is only a debugging/workaround path; it must not be used as proof that real user-like clicking works or to bypass prompt stop boundaries. This diagnostic is only for standalone top-level direct click calls; `find` locator clicks and native batch/QA click steps remain upstream-owned behavior.
639
645
 
640
646
  `promptGuard` may appear on wrapper-blocked calls only for concrete, machine-checkable prompt requirements. `reason: "requested-artifacts-missing-before-close"` blocks `close` / `quit` / `exit` when the prompt used a direct screenshot/recording creation phrase with a destination such as `here`, `at`, `as`, or `to` and the session artifact manifest has not verified that exact path; a destination heading can carry that intent across contiguous plain or Markdown-bulleted path-only list lines. Bare, review-only, fenced-reference, conditional, permissive/uncertain, directly negated, and Pi clipboard/attachment image/video paths are treated as input, not output requirements, while subordinate requirements such as “do not close until you save” remain output intent. A recording-availability qualifier may precede, appear within, or follow its path/list and is scoped through the next path boundary; if the same path appears more than once, any required occurrence takes precedence. Markdown-link destinations resolve to the destination path. Explicitly optional artifacts are not close requirements. The classifier is deliberately conservative, so use wording such as `Save a screenshot here: <path>` when machine-enforced close blocking matters. Optional recording paths are only required when recording appears available. The wrapper does **not** parse broad user/business intent such as “do not place the order” or “do not post anything” into click/key blocks; agents must follow those instructions themselves. Prompt guards return `failureCategory: "policy-blocked"` and `validationError` text instead of invoking upstream.
641
647
 
@@ -787,11 +793,11 @@ Matching requires the actual namespace/session, expected path and known recordin
787
793
 
788
794
  ## Browser-independent read confirmations
789
795
 
790
- `details.readConfirmation` stores only native control-response provenance for an actual explicit URL read: `{ id, sessionName, namespace?, source: "native-explicit-url-read", state: "pending" | "cleared", capabilities? }`. Page content, nested JSON text and bare DOM reads do not establish this provenance. It is replayed in existing per-session transcript state on resume/branch changes and retired by successful confirmation/denial or session close.
796
+ `details.readConfirmation` retains native control-response provenance in the existing per-session transcript state: `{ id, sessionName, namespace?, source, state: "pending" | "cleared", capabilities?, command?, action?, refSnapshotFresh?: true }`. `source: "native-guarded-action"` carries only the compact originating command and native action, never argv or a command replay blob. `source: "native-explicit-url-read"` remains specific to explicit URL reads. Page content, eval results and JSON-looking text cannot establish either provenance.
791
797
 
792
- Routing uses the actual native session, including `default` when the original read did not allocate a managed browser. Explicit caller session/namespace choices still win, and code calls cannot borrow another session's confirmation. Legacy native prompts retain this routing. Only a control response with `capabilities.readRequiresConfirmation: true` also proves native explicit-ID matching and enables matching confirm/deny without page helpers or managed-browser replacement. Legacy/unproven and DOM confirmations retain normal page checks. The advertised capability requires native ID validation before consuming a pending action, so a stale read ID cannot approve a later DOM action. A failed or expired confirmation returns exact session-status guidance, not an automatic retry of another ID.
798
+ Routing preserves the actual native session, including `default` for an unallocated explicit read, through resume/branch replay. Explicit caller identities and the code cell's fixed identity still win. An exact pending ID/canonical-identity match for a genuine guarded control suppresses page/pinning helpers that could replace the native pending slot; it neither proves native ID validation nor grants browser independence. Completed guarded actions and confirmed native batch rows use ordinary page/ref reconciliation, including click/eval/tab effects and successful close retirement through direct and code replay. Only a matching first effective guarded batch decision suppresses pre-command helpers; raw arguments still take precedence over stdin, and later page/ref/unknown-target checks remain. Requested-action pending is not labelled as a hidden helper; helper-origin pending retains its own diagnostic, and an early helper decision explicitly states that the requested command was not dispatched. Only explicit URL-read `capabilities.readRequiresConfirmation: true` grants browser-independent confirm/deny and promises native ID validation. Legacy URL reads and unrelated/nonmatching decisions keep normal checks. Failed or expired confirmations offer exact session inspection, never automatic approval of another ID. A genuine `tab_new` control may include the paired transport fields `after_confirmation: ["open", <original requested URL>]` and nonempty `guidance`; other extra fields or mismatched/malformed continuations do not grant confirmation provenance. The original URL is read with ordinary command operand handling, including preceding labels and headers. Valid conditional guidance stays model-visible and redacted: approval creates the blank/labeled tab, and only a separate caller-requested navigation follows; denial must not navigate. These fields are not stored as command replay, automatically executed, or accepted from page/eval/HTTP content.
793
799
 
794
- A confirmed HTTP failure is a tool failure regardless of that capability, including inside a batch. A pending confirmation is not success even when the native outer envelope says otherwise. If a new DOM confirmation replaces a pending read, the old read marker clears and the new confirmation keeps its own approve/deny actions and normal page checks.
800
+ A failed inner native confirmed action is a tool failure, including inside a batch, even when the outer confirmation succeeds. Nested confirmations stay pending and retain the original compact command provenance. A new native guarded control replaces the previous marker and supplies its own exact actions. A completed confirmed full snapshot installs returned refs in direct and ordered batch results; pending, denied, failed or partial captures do not grant freshness. When it is the final completed row, `refSnapshotFresh: true` allows the next same-identity call to reuse that sample once instead of requesting another gated freshness snapshot. Semantic role click/check/fill and unique role/label select use that same eligible capture with its editable metadata and unchanged action payloads. The call consumes the marker, including across direct/code/batch replay; any intervening same-identity call consumes it too. Pending snapshot controls use the ordinary confirmation-required presentation and executable actions; they are not empty-page observations. Target, membership, invalidation and native generation checks remain. On this one use, required live verification compares the complete capture URL, including fragments, and rejects refs when it changes—including wrapper-owned managed sessions and warm sessions without resume. Ordinary ref/tab URL comparisons remain fragment-insensitive. A DOM-only rename on the same page after the approved sample is not resampled on this one use; later ref operations retain ordinary freshness checks. Confirmed close also retires attachment state on live and resumed paths. If a cold managed reopen helper is pending, the result exposes that decision with `agentBrowserStarted: false` for the requested getter, unknown page target and invalid refs; it does not dispatch the getter or replay open. Successful confirm can then reconcile the native resulting target before immediate title/body inspection.
795
801
 
796
802
  ## High-value result rendering
797
803