pi-agent-browser-native 0.6.9 → 0.6.11
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.
- package/CHANGELOG.md +48 -0
- package/README.md +62 -19
- package/dist/extensions/agent-browser/index.js +424 -451
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +6 -7
- package/dist/extensions/agent-browser/lib/argv-grammar.js +7 -1
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +4 -8
- package/dist/extensions/agent-browser/lib/command-policy.js +41 -2
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +15 -2
- package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -1
- package/dist/extensions/agent-browser/lib/input-modes/script.js +3 -2
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +42 -12
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +3 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +6 -14
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +14 -25
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +12 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +3 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +38 -31
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +60 -20
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery.js +161 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +5 -5
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +2 -4
- package/dist/extensions/agent-browser/lib/orchestration/native-session-defaults.js +68 -0
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +41 -6
- package/dist/extensions/agent-browser/lib/page-target-validation.js +9 -5
- package/dist/extensions/agent-browser/lib/playbook.js +13 -12
- package/dist/extensions/agent-browser/lib/process-environment.js +26 -8
- package/dist/extensions/agent-browser/lib/process.js +8 -5
- package/dist/extensions/agent-browser/lib/read-confirmation.js +59 -0
- package/dist/extensions/agent-browser/lib/recording-reservations.js +11 -1
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +8 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +6 -5
- package/dist/extensions/agent-browser/lib/results/categories.js +4 -2
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +76 -57
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +19 -8
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +5 -25
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +40 -38
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +1 -0
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +3 -3
- package/dist/extensions/agent-browser/lib/results/presentation.js +38 -9
- package/dist/extensions/agent-browser/lib/results/recording.js +50 -0
- package/dist/extensions/agent-browser/lib/runtime.js +72 -20
- package/dist/extensions/agent-browser/lib/session-page-state.js +24 -8
- package/dist/extensions/agent-browser/lib/temp.js +4 -0
- package/dist/scripts/agent-browser-target.mjs +1 -1
- package/docs/ARCHITECTURE.md +29 -12
- package/docs/COMMAND_REFERENCE.md +67 -31
- package/docs/RELEASE.md +6 -4
- package/docs/SUPPORT_MATRIX.md +24 -16
- package/docs/TOOL_CONTRACT.md +82 -28
- package/package.json +1 -1
- package/scripts/agent-browser-capability-baseline.mjs +10 -3
- package/scripts/agent-browser-target.mjs +1 -1
- package/scripts/prepare.mjs +2 -4
package/docs/TOOL_CONTRACT.md
CHANGED
|
@@ -32,6 +32,24 @@ The tool also needs an operating playbook, not just a capability list. The model
|
|
|
32
32
|
|
|
33
33
|
The native command reference in `docs/COMMAND_REFERENCE.md` is driven by the same pattern: `scripts/agent-browser-target.mjs` owns the runtime version and `scripts/agent-browser-capability-baseline.mjs` imports it alongside help/doc inventory; selected regions are generated into the Markdown by `npm run docs -- command-reference write`, and `npm run docs` plus `npm run verify -- command-reference` catch drift (the latter also samples the installed `agent-browser` on `PATH`). Maintainer workflow details live in `AGENTS.md` under upstream capability baseline.
|
|
34
34
|
|
|
35
|
+
## Host execution hook
|
|
36
|
+
|
|
37
|
+
An SDK host can import the package's compiled `dist/extensions/agent-browser/index.js` default export and register it once through a Pi extension factory:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
agentBrowserExtension(pi, {
|
|
41
|
+
async beforeExecute(toolCallId, ctx) {
|
|
42
|
+
await saveHostState(toolCallId, ctx.signal);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`beforeExecute` is an optional host callback, not a tool input or package config field. It receives the original outer Pi tool-call ID and the current `ExtensionContext`, with `ctx.signal` set to the dispatched call's abort signal. Hosts must honor that signal when waiting so Stop remains responsive.
|
|
48
|
+
|
|
49
|
+
The extension awaits it after input resolution succeeds and before each non-script dispatch, including each accepted `browser(...)` call inside a script. The script wrapper itself does not call it; inner calls retain the original outer ID and use their own cancellation signals. The existing script queue remains serial even for `Promise.all`, so a completed inner call's files are available to the next callback. Internal helper probes and cleanup do not call it, nor does each row of a native `batch` get a separate callback. Script setup may probe the upstream version before the first inner callback.
|
|
50
|
+
|
|
51
|
+
Supplying the callback registers `agent_browser` with Pi's native `executionMode: "sequential"`. Pi then finishes earlier sibling tools before entering the callback and dispatching the browser call. Rejection prevents that dispatch: direct calls become Pi tool errors, while script inner calls receive the existing failed-call envelope. The host owns saving/retry policy; the extension adds no checkpoint store, retry loop, or deadline. Existing script deadlines and cleanup still apply. Omitting the callback preserves ordinary scheduling and behavior; the separate web-search tool is unchanged.
|
|
52
|
+
|
|
35
53
|
## Optional companion web search
|
|
36
54
|
|
|
37
55
|
`agent_browser_web_search` is a separate custom tool, not an `agent_browser` input mode. It is available when the extension can see at least one configured/resolvable Exa or Brave credential source from `~/.pi/config/pi-agent-browser-native/config.json`, `.pi/config/pi-agent-browser-native/config.json`, `PI_AGENT_BROWSER_CONFIG`, or the `EXA_API_KEY` / `BRAVE_API_KEY` environment fallbacks, and runtime execution still checks that the final available merged config has not set `webSearch.enabled` to `false`. Config layers merge global → project → `PI_AGENT_BROWSER_CONFIG` override; under Pi 0.84.0+, globally installed and CLI-loaded copies read `.pi/config/...` when Pi trust allows that project layer, and they skip the project layer when Pi reports the project is untrusted or when launched with `--no-approve`. Disable scope is explicit: a global disable is a normal user default, a project disable applies to one repo, and an override file with `webSearch.enabled: false` is the highest-priority hard disable for that run. Credential sources may be plaintext, `$ENV_VAR` / `${ENV_VAR}` interpolation, escaped literals, or command sources such as `"!op read 'op://Private/Exa/API Key'"` from any loaded config layer; they make the tool available without exposing the value in status text, and command values resolve when the tool executes. Browser profile/executable config uses the same paths and emits prompt guidance from the highest-priority loaded layer, including project config when that layer is loaded.
|
|
@@ -144,6 +162,8 @@ The extension always plans normal browser commands with `--json` prepended in `e
|
|
|
144
162
|
|
|
145
163
|
Upstream 0.36.0 exposes page-registered tools through ordinary `args`: `webmcp list`, `webmcp invoke <tool> [--params <json|@file>] [--frame <frame-id>] [--detach] [--timeout <ms>]`, `webmcp result <id>`, and `webmcp cancel <id>`. Locally managed Chrome enables the experimental CDP feature by default. `--no-webmcp` / `AGENT_BROWSER_NO_WEBMCP` / upstream config `noWebmcp` disables it; attached browsers, providers, Lightpanda, Safari/iOS, and older Chrome builds may return upstream `webmcp_unsupported`.
|
|
146
164
|
|
|
165
|
+
Native 0.37 can include `data.webmcp` on successful navigation. When `available` is true and `toolCount` is a positive integer, the page summary shows the native availability hint and recommends `webmcp list`. Raw metadata remains in `details.data`; absent, unavailable, zero or invalid counts add no hint. The wrapper does not run a discovery probe.
|
|
166
|
+
|
|
147
167
|
The wrapper keeps this as thin CLI pass-through. `webmcp list` is read-only. `invoke`, `result`, and `cancel` may run page code that mutates, rerenders, or navigates, so the wrapper rechecks the live target, emits the normal `pageChangeSummary` and `inspect-after-mutation` follow-up when applicable, and stores `refSnapshotInvalidation.reason: "page-transition"`; old page-scoped refs remain blocked until a fresh `snapshot -i`. A direct or batched call whose result is still `pending`, or a failed `result` / `cancel` attempt made while that target is unknown, does not treat the immediate URL probe or a same-batch snapshot as stable: `details.sessionTabTargetUnknown` stays true until a successful settlement, `get url`, or explicit navigation verifies the page. Its `details.nextActions` replaces the blocked snapshot suggestion with `verify-page-target-after-pending-webmcp` (`get url`); the action warns that URL inspection does not settle the detached page tool. Inside one `batch --bail`, put `get url` after a completed WebMCP mutation and before `snapshot -i`; a snapshot directly against the unknown target remains blocked. Detached invocation ids and page-returned data remain in `details.data`. When top-level `timeoutMs` is omitted, `webmcp invoke` and `webmcp result` extend the wrapper subprocess watchdog to the upstream `--timeout` value plus a small grace window, including effective raw-argument batch rows (which take precedence over stdin exactly as upstream does).
|
|
148
168
|
|
|
149
169
|
`--no-webmcp` is launch-scoped for both bare/`true` and explicit `false` values. Put it on the first call for a session or use `sessionMode: "fresh"` after an implicit managed session exists. The upstream `webmcp-gen` skill is available through stateless `skills get webmcp-gen`; an external MCP server can opt in with `mcp --tools core,webmcp`, but bare long-running `mcp` remains unsuitable for a one-shot Pi tool call.
|
|
@@ -168,7 +188,7 @@ Upstream 0.35.2 adds `dashboard start --allowed-origins <origins>` and `AGENT_BR
|
|
|
168
188
|
|
|
169
189
|
<!-- agent-browser-playbook:start shared-guidelines -->
|
|
170
190
|
<!-- Generated from extensions/agent-browser/lib/playbook.ts. Run `npm run docs -- playbook write` to update. -->
|
|
171
|
-
- Use top-level script only for one-shot loops, conditional page branches, or multi-page aggregation that would otherwise require several calls: await browser({ args, stdin?, timeoutMs? }), branch on its ok field, and emit one bounded JSON value. Script gets an isolated non-profile browser session that is always closed, cannot use caller session/namespace/lifecycle/attachment controls, inherited agent-browser launch/proxy settings, or host APIs, and is not a reusable named recipe. One top-level approval can authorize up to 25 inner calls, so inspect the full source before approval. Use args/job/qa for ordinary linear flows.
|
|
191
|
+
- Use top-level script only for one-shot loops, conditional page branches, or multi-page aggregation that would otherwise require several calls: await browser({ args, stdin?, timeoutMs? }), branch on its ok field, and emit one bounded JSON value. Script gets an isolated non-profile browser session that is always closed, cannot use caller session/namespace/lifecycle/attachment controls, inherited agent-browser launch/proxy settings, user/project config or explicit --config, or host APIs, and is not a reusable named recipe. One top-level approval can authorize up to 25 inner calls, so inspect the full source before approval. Use args/job/qa for ordinary linear flows.
|
|
172
192
|
- Standard workflow: open the page, snapshot -i, interact using current @refs from that snapshot, and re-snapshot after navigation, scrolling, rerendering, or other major DOM changes because refs are page-scoped; the wrapper fails mutation-prone stale/recycled refs before upstream can silently target a different current-page element. On dense pages, use wrapper-side snapshot -i --search <text> or snapshot -i --filter role=<role> to render matching refs while preserving the full ref map in details.refSnapshot, add snapshot --viewport when scroll position or above/below-fold context matters, and add snapshot --diff when a quick before/after ref-map delta would prevent reading a full spill file.
|
|
173
193
|
- For ordinary forms from one snapshot, batch multiple fill @refs before the submit/click step to avoid serial tool calls; if a fill may autosubmit, navigate, or rerender later fields, split the flow and refresh refs first.
|
|
174
194
|
- Do not use browser automation to drive public search-engine forms such as Google for discovery; headless jobs that type a query and press Enter can be redirected to anti-bot or CAPTCHA pages. Prefer agent_browser_web_search for live discovery, then agent_browser on a target URL. Do not attempt CAPTCHA bypass.
|
|
@@ -177,13 +197,13 @@ Upstream 0.35.2 adds `dashboard start --allowed-origins <origins>` and `AGENT_BR
|
|
|
177
197
|
- For desktop or host-controlled rich inputs, if semanticAction fill misses, refresh refs and prefer a current editable @ref from details.richInputRecovery or the latest snapshot; focus or click that ref, then use keyboard type for framework-controlled editors that require real key events. keyboard inserttext is paste-like and 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 with Enter or a submit button unless the user flow explicitly calls for it.
|
|
178
198
|
- Do not assume Playwright selector dialects such as text=Close or button:has-text('Close') are supported wrapper syntax unless current upstream agent-browser behavior has been verified.
|
|
179
199
|
- For authenticated or user-specific content explicitly requested by the user, such as feeds, inboxes, account pages, or private dashboards, use a real profile only when the user/config asks for it or profiles have been inspected; do not assume --profile Default exists on every machine. Do not use a real profile for public pages just because they are dashboards. Treat visible page content from real profiles as model-visible transcript data. On macOS, copied Chrome profiles may omit encrypted cookies, so profile selection alone is not proof of authentication; verify the target page and use a user-approved headed login once when needed. Use --auto-connect only if profile-based reuse is unavailable or the task is specifically about attaching to a running debug-enabled browser. If profile/user-data-dir resolution fails, stop retrying opens, run profiles and/or doctor through agent_browser, then report what the user needs to configure.
|
|
180
|
-
-
|
|
200
|
+
- Use bare calls for a configured native shared session; the wrapper honors its session/namespace across Pi agents without owning quit cleanup or idle policy. Otherwise use the implicit session for routine work. Coordinate shared tabs and do not close another agent's browser.
|
|
181
201
|
- When using launch-scoped flags (--auto-connect, --allowed-domains, --namespace, --cdp, --ca-cert, --no-ca-cert, --enable, --executable-path, --webgpu, --no-webmcp, --init-script, --idle-timeout, --args, --user-agent, --headed, --device, --profile, --provider, -p, --session-name, --restore, --restore-save, --restore-check-url, --restore-check-text, --restore-check-fn, --state), put them on the first command for that session. If you intentionally use an explicit --session, keep using that same explicit session for follow-ups.
|
|
182
202
|
- Caller-owned explicit sessions are serialized per effective canonical namespace/session inside this extension while live URL checks, semantic-action snapshots, and the requested command run. For raw batches whose later content step depends on navigation, use exact batch --bail or split the calls; unsafe continue-after-navigation-failure shapes are rejected before the batch runs.
|
|
183
203
|
- After a successful `connect`, `--cdp`, or enabled `--auto-connect` call, verify with get url and keep using the resulting session without repeating the attach flag. The wrapper remembers that attachment across active-branch reload/resume and live-checks the URL before later page reads/interactions because an attached browser can drift externally; caller config, file access, launch arguments, and environment pass through unchanged. A successful close clears the marker. When several named sessions share one Chrome, pass --pin-tab once (AGENT_BROWSER_PIN_TAB) so a closed bound tab fails as tab_gone instead of acting on a neighbor; recover with tab new or tab list. --no-pin-tab turns the sticky pin off. tab list includes each tab's CDP targetId, accepted as a tab ref.
|
|
184
204
|
- If you already used the implicit session and now need launch-scoped flags (--auto-connect, --allowed-domains, --namespace, --cdp, --ca-cert, --no-ca-cert, --enable, --executable-path, --webgpu, --no-webmcp, --init-script, --idle-timeout, --args, --user-agent, --headed, --device, --profile, --provider, -p, --session-name, --restore, --restore-save, --restore-check-url, --restore-check-text, --restore-check-fn, --state), retry with top-level sessionMode set to fresh or pass an explicit --session for the new launch; never pass --session-mode inside args. After a successful unnamed fresh launch, later auto calls follow that new session.
|
|
185
205
|
- For WebGPU pages, use args ["--webgpu", "open", "<url>"] on a fresh local browser launch; use doctor --webgpu (or --headed on Linux/Windows capture paths) to prove rendering before trusting a non-black screenshot. WebGPU cannot be combined with --cdp, --auto-connect, or provider launches unless --webgpu false overrides an enabled config/environment default.
|
|
186
|
-
- For experimental WebMCP page tools, use webmcp list, then webmcp invoke <tool> with --params and optional --frame/--detach/--timeout; use webmcp result or cancel for detached calls. Locally managed Chrome enables WebMCP by default. --no-webmcp is launch-scoped and requires a fresh session; invoke/result/cancel can mutate or navigate, so refresh snapshot refs afterward.
|
|
206
|
+
- For experimental WebMCP page tools, use webmcp list, then webmcp invoke <tool> with --params and optional --frame/--detach/--timeout; use webmcp result or cancel for detached calls. Locally managed Chrome enables WebMCP by default; a positive navigation hint means the page has tools to list. --no-webmcp is launch-scoped and requires a fresh session; invoke/result/cancel can mutate or navigate, so refresh snapshot refs afterward.
|
|
187
207
|
- For --allowed-domains, use a fresh local Chrome context. Upstream rejects CDP/auto-connect, profiles, restore/state replay, direct-page providers, iOS/Safari, and startup/profile Chrome args because they cannot guarantee containment; Chromium also disables RTCPeerConnection while the allowlist is active.
|
|
188
208
|
- For React introspection, launch the page with --enable react-devtools before first navigation, then use react tree, react inspect <fiberId>, sourceLookup candidates for local UI source hints, react renders start/stop, or react suspense; sourceLookup is experimental and reports confidence/evidence instead of guaranteed DOM-to-file mappings. For failed fetches and APIs, networkSourceLookup (experimental) correlates failed network requests with initiator metadata and bounded workspace URL literals—candidates only, not definitive blame. Use vitals [url] for Core Web Vitals and hydration timing, and pushstate <url> for client-side SPA navigation.
|
|
189
209
|
- For first-navigation setup, use open without a URL plus network route --resource-type <csv>, cookies set --curl <file>, or --init-script/--enable before navigate/opening the target page.
|
|
@@ -205,7 +225,7 @@ Upstream 0.35.2 adds `dashboard start --allowed-origins <origins>` and `AGENT_BR
|
|
|
205
225
|
- 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.
|
|
206
226
|
- 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.
|
|
207
227
|
- Respect explicit user stop boundaries yourself. When the surrounding authenticated employee or automation context is explicitly unattended/auto-approved, ordinary non-destructive form submissions within the requested flow may proceed without separate confirmation. Still require explicit authorization for purchases, production-control actions, destructive or irreversible actions, and account, security, or privacy changes. The wrapper does not infer broad business intent from prompt text; details.promptGuard is reserved for concrete artifact-before-close checks.
|
|
208
|
-
-
|
|
228
|
+
- Recording needs ffmpeg on PATH before start. Current upstream checks it at startup; older natives may defer failure. A pending recording is not verified output.
|
|
209
229
|
- Do not call --help or other exploratory inspection commands unless the user explicitly asks for them or debugging the browser integration is necessary.
|
|
210
230
|
<!-- agent-browser-playbook:end shared-guidelines -->
|
|
211
231
|
|
|
@@ -265,7 +285,7 @@ Examples:
|
|
|
265
285
|
- browser calls are serialized even when source uses `Promise.all`; the maximum is 25 attempted calls. Source and final serialized output are each capped at 64 KiB; cumulative child/parent IPC is bounded. Final data is redacted, serialized without pretty-print amplification, and byte-checked again before presentation; excessive depth or post-redaction growth becomes `failureCategory: "validation-error"` with data omitted.
|
|
266
286
|
- default top-level `timeoutMs` is 120,000 ms for script; the hard ceiling is 300,000 ms. Every inner timeout is clamped to the remaining outer deadline. Abort, timeout, Pi branch change, quit, reload, and child failure cascade to the active inner call, wait for isolated-session cleanup, then terminate and reap the sandbox child with a bounded SIGTERM/SIGKILL sequence.
|
|
267
287
|
- before the first accepted inner browser call, the wrapper appends a model-invisible Pi custom-entry lease containing only a strict wrapper-generated `piab-script-<uuid>` session name, exact close argv, `launchAttempted: true`, and cleanup state. Script mode therefore requires a persisted Pi session and fails validation under `--no-session`.
|
|
268
|
-
- every invocation uses a unique wrapper-owned session in the empty canonical namespace, with managed restore disabled. It never reads, replaces, or updates the extension-managed implicit conversation session. Inner identity/lifecycle/attachment/local commands, nested `batch`, nested top-level modes, `--session`, `--namespace`, profile/state/restore/provider/CDP/raw-args/init/extension launch controls, and local/sessionless commands are rejected before upstream spawn. Every script-owned helper and cleanup subprocess also clears ambient `AGENT_BROWSER_*` and standard proxy variables before wrapper-owned namespace, timeout, and compatibility values are applied.
|
|
288
|
+
- every invocation uses a unique wrapper-owned session in the empty canonical namespace, with managed restore disabled. It never reads, replaces, or updates the extension-managed implicit conversation session. Inner identity/lifecycle/attachment/local commands, nested `batch`, nested top-level modes, `--session`, `--namespace`, `--config`, profile/state/restore/provider/CDP/raw-args/init/extension launch controls, and local/sessionless commands are rejected before upstream spawn. Every script-owned helper and cleanup subprocess also clears ambient `AGENT_BROWSER_*` and standard proxy variables and uses an empty private temporary native config, bypassing HOME/project profile defaults, before wrapper-owned namespace, timeout, and compatibility values are applied.
|
|
269
289
|
- the wrapper closes the isolated session in `finally`, including after error, timeout, abort, or branch change. `session_tree` and `session_shutdown` abort active scripts and await their normal cleanup before branch restoration or shutdown cleanup continues. On session start or branch change, exact non-closed leases from the active branch are then retried; forged names or close argv are ignored.
|
|
270
290
|
- details include `scriptRun.callCount`, disjoint `successfulCallCount`, ordinary dispatched `failedCallCount`, `preDispatchRejectedCallCount`, and emission/timeout/abort fields plus bounded `scriptSteps` category/summary rows. Any pre-dispatch policy or validation rejection fails the top-level tool result even when source handles the returned `{ ok: false }` envelope. When a browser launch was attempted, `scriptSession: { sessionName, cleanup: "closed", closeCommandArgs, launchAttempted: true }` and the compact prose both confirm successful cleanup. Uncaught user-source exceptions use `failureCategory: "script-error"`. Cleanup failure overrides every script outcome with `failureCategory: "cleanup-failed"`, `scriptSession.cleanup: "failed"`, a redacted error, and `nextActions[0].id: "close-script-session-after-cleanup-failure"` carrying the exact close args.
|
|
271
291
|
- this is not a reusable recipe layer: there is no script name, registry, persistence, cross-call state, host module import, or workflow versioning surface. Pi approval applies to the one visible top-level tool input, which may issue up to 25 inner calls. The collapsed Pi tool row renders a bounded terminal-safe source preview with line breaks marked as `↵`; expand the row to inspect the full terminal-safe script before approving it. Rendering normalizes JavaScript CR, U+2028, and U+2029 line terminators to visible newlines and replaces stripped terminal/directional/zero-width controls with a visible marker so sanitization cannot join a line comment to executable source or silently hide removed characters.
|
|
@@ -316,7 +336,7 @@ If a compiled `semanticAction` fails with `failureCategory: "selector-not-found"
|
|
|
316
336
|
|
|
317
337
|
If a compiled `semanticAction` `find` action fails with `failureCategory: "stale-ref"`, `details.nextActions` includes `retry-semantic-action-after-stale-ref` with the same redacted compiled argv as `details.compiledSemanticAction` in `params.args` (any leading `--session` pair from `semanticAction.session`, then the `find` tokens). The wrapper appends that entry **after** any `refresh-interactive-refs` snapshot step from `buildAgentBrowserNextActions` in `extensions/agent-browser/lib/results/action-recommendations.ts` (see `extensions/agent-browser/index.ts` where `nextActions` is merged). That retry is only offered because the semantic target is stable and the stale-ref error proves the previous action did not execute; `select` shorthands with stale `@e…` selectors and direct stale `@e…` commands still return refresh guidance instead of an unsafe blind retry.
|
|
318
338
|
|
|
319
|
-
For direct page-scoped refs (`@eN`, `eN`, or `ref=eN`), successful `snapshot` results record `details.refSnapshot` with the latest ref ids and page target for the session. A failed session `snapshot` whose upstream error says `No active page` clears that session’s prior ref snapshot and records `details.refSnapshotInvalidation.reason: "no-active-page"`; any upstream-executed `record start` attempt (direct or inside a batch, including one that fails with `Recording already active`,
|
|
339
|
+
For direct page-scoped refs (`@eN`, `eN`, or `ref=eN`), successful `snapshot` results record `details.refSnapshot` with the latest ref ids and page target for the session. A failed session `snapshot` whose upstream error says `No active page` clears that session’s prior ref snapshot and records `details.refSnapshotInvalidation.reason: "no-active-page"`; any upstream-executed `record start` attempt (direct or inside a batch, including one that fails with `Recording already active`, to protect older supported natives that swap the page before that check) or `record restart` with a URL operand clears it and records `details.refSnapshotInvalidation.reason: "page-transition"`; a restart without a URL (including `--fps` options alone) keeps the current page and refs; mutation-prone `@e…` preflight then fails with `failureCategory: "stale-ref"` until a later successful `snapshot -i` records fresh refs. Before page-scoped ref commands such as `get text`/`html`/`value`/`attr`/`box`/`styles`, `click`, `fill`, `check`, `select`, `download`, drag/upload actions, upstream ref-resolving reads and captures (`is`, `screenshot`, `highlight`, `scroll`, `frame`, `diff screenshot`), or equivalent batch steps run, the wrapper rejects refs from an older page target, refs absent from the latest same-page snapshot, or refs from an invalidated snapshot state. Batch steps are scanned from the source upstream actually executes: raw batch argument strings exclusively when any exist (upstream filters only the exact `--bail` token, so `--bail=true` stays a raw command), stdin steps only otherwise, so `batch "click @e1"` is guarded and stdin refs are not falsely rejected when upstream would ignore that stdin. Tab recovery verifies/selects the intended tab before semantic/ref helpers, then dispatches the caller's original argv/stdin. It does not force a continue-on-error batch to fail fast or turn literal operands into outer flags. A missing target or failed selection stops before any page-dependent user step. Local commands and explicit `connect` / `state load` recovery do not need the old tab, including the first effective batch row; later content still needs the normal page verification, and replacement does not supply fresh refs. Local success does not clear restored-target protection for the next page action. Same-tab checks preserve upstream refs and frame scope. Both pinned and unpinned failures retain `batchSteps`, `batchFailure`, and the visible failure roll-up. Artifact/screenshot preflights also skip upstream-ignored stdin rows. Getter batches receive the same same-page freshness check so a recycled `@ref` cannot silently read a different control after an in-place rerender. When a prior snapshot and session are available and those checks apply, ref-consuming calls add one extra `snapshot -i` preflight per top-level call or batch. Batching shares the probe across rows; it does not eliminate it. Only ref-resolving selector operands are guarded: ref-looking fill/type text, select values, file paths, attributes, and non-selector flag values remain literal. `get count` uses CSS/XPath, and `diff snapshot --selector` uses CSS; a bare `e999` remains a tag selector in those positions, not a ref. Commands whose operands upstream never resolves as refs (`wait`, `a11y`, `find`, `press`/`key`, `keyboard`, `mouse`) are not ref-guarded. Selector flags and positional selectors after `--new-tab` / `--full` remain guarded. A `batch` that times out or returns unparseable output after executing is treated conservatively: when its planned steps include a recording start or URL-bearing restart, the wrapper still records the `page-transition` invalidation. This is a best-effort wrapper guard against upstream ref-number recycling after navigation; it does not prove the DOM stayed unchanged after the snapshot. Refresh with the session-aware `refresh-interactive-refs` next action before retrying.
|
|
320
340
|
|
|
321
341
|
Examples:
|
|
322
342
|
|
|
@@ -644,9 +664,10 @@ For `eval --stdin`, put the script in the top-level `stdin` field. The wrapper n
|
|
|
644
664
|
### `outputPath`
|
|
645
665
|
|
|
646
666
|
- type: `string`
|
|
647
|
-
- optional; can be used with
|
|
667
|
+
- optional; can be used with successful browser results, most often `eval --stdin`, `get text`, `get html`, `snapshot`, or diagnostic captures. Recording results also export on failure, pending finalization, timeout and recovery; unrelated failed extractions remain unwritten.
|
|
648
668
|
- workspace-relative paths resolve against the Pi session cwd; absolute paths are used as-is; a leading `@` is stripped for consistency with Pi file arguments
|
|
649
669
|
- after the upstream command completes, the wrapper writes `details.data` when present, otherwise the model-facing text content; objects/arrays are written as pretty JSON with a trailing newline and strings are written as-is. If a direct `details.data` value, a `batch` row's `result`, or the whole batch is compacted, the wrapper instead reads and serializes each full command-redacted pre-compaction payload only from the corresponding live `spill` entry in its own `details.artifactManifest` (`persistent-session` or `process-temp`). It never writes a compact metadata object as a substitute; any missing, evicted, malformed, or untrusted required spill makes the result fail and leaves `outputPath` unwritten.
|
|
670
|
+
- recording exports use `source: "recording-receipt"` and a JSON envelope containing `success`, `error`, `command`/`subcommand`, native session/namespace, `attempt`, `data`, `artifacts`, `artifactVerification`, and optional `recordingRecovery`. A recovered result may have `success: true` while `attempt.success: false` preserves the timeout/error; saving the receipt file never proves the video succeeded. Preflight failures can export an empty receipt with their failed attempt, without inventing an artifact.
|
|
650
671
|
- `outputPath` must not resolve to the same file as a screenshot, download, recording, or other browser artifact produced by that result, including dangling/existing symlink, hardlink, Unicode-fold, and platform-case aliases. When both destinations are known before launch, preflight rejects the call as `validation-error` without browser activity; if an alias becomes apparent only from the completed result, the writer preserves the browser artifact, rejects the result-data write, and reports `details.outputFile.status: "failed"`
|
|
651
672
|
- successful writes append `details.outputFile = { status: "saved", path, absolutePath, source, bytes }`; they also append a visible `Output file: …` line except when the caller explicitly passed upstream `--json`, where parseable JSON content is preserved and the saved-file notice lives only in `details.outputFile`. Write failures append `details.outputFile.status: "failed"`, remove success-only category fields, and mark the tool result failed without rolling back browser session state.
|
|
652
673
|
|
|
@@ -663,7 +684,7 @@ Example:
|
|
|
663
684
|
- managed-session daemon-policy inspection has its own fixed budget of up to 35 seconds before that process and is intentionally not shortened by `timeoutMs`, so a busy valid daemon does not become an unsafe false negative
|
|
664
685
|
- use for long opens, large snapshots, paced `job` typing, or captures that legitimately need more than the default watchdog
|
|
665
686
|
- explicit long `wait` steps are forwarded to upstream; top-level `timeoutMs` only controls the wrapper subprocess watchdog and should be at least the wait duration plus a small grace window when supplied manually
|
|
666
|
-
- when the watchdog fires, `details.timeoutMs`, `details.timedOut`, and possibly `details.timeoutPartialProgress` explain what was recovered. If the page target is unknown, standalone snapshot suggestions are removed and one session-scoped `verify-page-target-after-timeout` fail-fast batch (`get url`, then `snapshot -i`) appears in visible failure text and `details.nextActions`, so the returned recovery is executable under the same page-target guard.
|
|
687
|
+
- when the watchdog fires, `details.timeoutMs`, `details.timedOut`, and possibly `details.timeoutPartialProgress` explain what was recovered. Explicit URL reads and proven browser-independent read confirmations do not run timeout page probes. A `session info` timeout also leaves browser/page/ref state untouched and offers only `retry-session-info` for the same session/namespace, without claiming liveness. Uncertain recording stops instead use the bounded receipt recovery described below. If the page target is unknown, standalone snapshot suggestions are removed and one session-scoped `verify-page-target-after-timeout` fail-fast batch (`get url`, then `snapshot -i`) appears in visible failure text and `details.nextActions`, so the returned recovery is executable under the same page-target guard.
|
|
667
688
|
|
|
668
689
|
Example:
|
|
669
690
|
|
|
@@ -673,6 +694,8 @@ Example:
|
|
|
673
694
|
|
|
674
695
|
### `sessionMode`
|
|
675
696
|
|
|
697
|
+
A native `session` default from user/project JSON or `AGENT_BROWSER_SESSION` selects a caller-owned browser for `args`, `semanticAction`, `job`, `qa`, and lookups. Per-call flags still win. Like literal `--session`, this selection takes precedence over `fresh`; it reports `usedImplicitSession: false` and is not closed when Pi quits. Without a selected native session, the implicit/fresh behavior below is unchanged. `qa.attached` can inspect this shared current session using the same live target checks. See [shared browser defaults](../README.md#shared-browser-defaults).
|
|
698
|
+
|
|
676
699
|
- type: `"auto" | "fresh"`
|
|
677
700
|
- optional
|
|
678
701
|
- default: `"auto"`
|
|
@@ -764,7 +787,7 @@ Recommended details:
|
|
|
764
787
|
Stable category fields are part of the machine-readable contract:
|
|
765
788
|
|
|
766
789
|
- `resultCategory`: always either `"success"` or `"failure"`.
|
|
767
|
-
- `successCategory`: present on successful results. Current values are `"completed"`, `"artifact-pending"`, `"artifact-saved"`, `"artifact-unverified"`, and `"inspection"`. `artifact-pending` means a recording started but its file is not expected until `record stop`; use the exact `stop-pending-recording` next action and verify the resulting file. Dispatched `record start` and URL-bearing `record restart` attempts append one proactive `Page state:` warning, even on failure,
|
|
790
|
+
- `successCategory`: present on successful results. Current values are `"completed"`, `"artifact-pending"`, `"artifact-saved"`, `"artifact-unverified"`, and `"inspection"`. `artifact-pending` means a recording started but its file is not expected until `record stop`; use the exact `stop-pending-recording` next action and verify the resulting file. Dispatched `record start` and URL-bearing `record restart` attempts append one proactive `Page state:` warning, even on failure, describing conservative ref invalidation, not an observed page change; caller-requested `--json` carries it in `warnings`. Batch warnings require a reached result row. Preflight failures, missing binaries, help, plain restarts and unconfirmed planned rows do not claim a recording page change; the wrapper also invalidates the session’s prior ref snapshot (`refSnapshotInvalidation.reason: "page-transition"`) so old `@e…` refs fail as `stale-ref` until a fresh `snapshot -i`; that invalidation is attempt-scoped to protect older supported natives (0.37 normally keeps the active page and heap) and also covers `record restart` with a URL operand, while a plain `record restart` keeps the page and refs. Failed results also retain that action whenever their artifact rollup still contains a pending recording, except when the live daemon policy permits only cleanup: those results offer `close-pending-recording` instead, explicitly abandoning the unverified recording. `artifact-unverified` means upstream reported success but the merged `artifactVerification` summary still has unverified non-missing rows; inspect its counts and per-entry `state` / optional `limitation` before treating artifacts as durable evidence.
|
|
768
791
|
- `failureCategory`: present on failed results. Current values are `"aborted"`, `"artifact-missing"`, `"cleanup-failed"`, `"confirmation-required"`, `"download-not-verified"`, `"missing-binary"`, `"parse-failure"`, `"policy-blocked"`, `"qa-failure"`, `"script-error"`, `"selector-not-found"`, `"selector-unsupported"`, `"stale-ref"`, `"tab-drift"`, `"tab-gone"`, `"timeout"`, `"upstream-error"`, and `"validation-error"`. `artifact-missing` means upstream reported a saved/completed artifact path, but the wrapper verified the non-pending file is absent and failed closed.
|
|
769
792
|
|
|
770
793
|
For `script`, the top-level category describes the whole orchestration and cleanup, not the last inner call. `details.scriptRun` reports `callCount`, `successfulCallCount`, `failedCallCount`, `preDispatchRejectedCallCount`, `emitCount`, and timeout/abort flags when applicable. `script-error` means the caller's script source threw or rejected; browser subprocess and wrapper protocol failures remain `upstream-error` unless a more specific category applies. `details.scriptSteps[]` preserves bounded redacted per-call category/summary rows rather than replaying full inner tool results. `details.scriptSession` reports the exact isolated-session cleanup lease state only after the first accepted inner call; successful no-browser scripts omit it. A cleanup failure always wins over an otherwise successful or failed script so the leaked browser identity is not hidden.
|
|
@@ -787,9 +810,9 @@ Ref preflight details (command taxonomy in `extensions/agent-browser/lib/command
|
|
|
787
810
|
- **URL alignment:** `refSnapshot.target.url` and the session’s current tab URL are compared via `targetsMatch` / `normalizeComparableUrl` in `extensions/agent-browser/index.ts`: values are trimmed, parsed as URLs when possible, compared **after dropping the `#fragment`**, and the query string remains significant. If either side lacks a `url`, `targetsMatch` treats the pair as matching so early-session calls are not blocked.
|
|
788
811
|
- **Batch stdin ordering:** user `batch` JSON is scanned in order. Any step whose first token satisfies `isRefInvalidatingBatchCommand` sets a latch that blocks later steps whose first token satisfies `isRefGuardedCommand` and that mention `@e…` refs, except for same-snapshot native form-control steps whose current snapshot role metadata identifies all refs as safe controls (`check`/`uncheck` or direct `click`/`tap` on checkbox or radio refs, and `select` on combobox refs). A step whose first token is `snapshot` clears that latch for subsequent steps (pre-spawn intent only; it does not wait for upstream success). These predicates read explicit command capability flags from `command-taxonomy.ts`: navigation/mutation verbs such as `open` / `goto`, `reload`, non-form `click`, and related upstream commands have `invalidatesBatchRefs`, and `record start` steps (any outcome), `record restart` steps with a URL operand, plus WebMCP `invoke` / `result` / `cancel` steps also set the latch because upstream swaps or navigates the active page; same-snapshot `fill` rows and the role-checked native form-control rows stay guarded against missing/stale refs but do not set the latch, allowing ordinary form batches before a final click/submit step. Direct `click`/`tap @e…` is only treated as a safe form-control row when every ref in that step is a latest-snapshot checkbox or radio; other click/tap refs remain invalidating. Ref-guarded commands accept page-scoped refs for interaction (`click`, `fill`, `download`, `scrollintoview` / `scrollinto`, and others centralized in the command taxonomy). Changing either capability requires updating this contract, [`docs/SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md) `RQ-0072`/`RQ-0087` notes, README and command-reference pitfalls, and `test/agent-browser.extension-validation.test.ts`.
|
|
789
812
|
|
|
790
|
-
**Presentation redaction (implementation map):** Successful non-`batch` tool calls and each successful `batchSteps[]` row run upstream `data` through `redactPresentationData` in `extensions/agent-browser/lib/results/presentation/diagnostics.ts`: `cookies` still walk objects/arrays and replace case-insensitive `value` keys with `"[REDACTED]"`; `storage` redacts values when the key or value looks credential-like (token, cookie, auth, secret, JWT, bearer/basic credential, high-entropy token-like string, or nested sensitive JSON) but keeps low-risk primitive QA values such as booleans, numbers, and short strings visible. Redacted storage entries add `valueRedacted` plus `valueRedactionReason` in `details.data`; diagnostic formatters mirror the same decision. Every other command’s payload is recursively scrubbed with `
|
|
813
|
+
**Presentation redaction (implementation map):** Successful non-`batch` tool calls and each successful `batchSteps[]` row run upstream `data` through `redactPresentationData` in `extensions/agent-browser/lib/results/presentation/diagnostics.ts`: `cookies` still walk objects/arrays and replace case-insensitive `value` keys with `"[REDACTED]"`; `storage` redacts values when the key or value looks credential-like (token, cookie, auth, secret, JWT, bearer/basic credential, high-entropy token-like string, or nested sensitive JSON) but keeps low-risk primitive QA values such as booleans, numbers, and short strings visible. Redacted storage entries add `valueRedacted` plus `valueRedactionReason` in `details.data`; diagnostic formatters mirror the same decision. Every other command’s payload is recursively scrubbed with the shared `redactSensitiveValue`, which redacts known sensitive key names and applies string-level sensitivity heuristics so network, diff, trace/profiler, stream, dashboard, chat, and other structured results do not echo bearer tokens, proxy credentials, or similar fields verbatim into `details.data`. Echoed `command` arrays in `details` and in batch roll-ups use `redactInvocationArgs` from `extensions/agent-browser/lib/runtime.ts` to mask trailing values for sensitive global flags (including `--body`, `--headers`, `--password`, and `--proxy`), preserve the special positional rules for `cookies set`, `storage local|session set`, and `set credentials`, and scrub other argv tokens for URLs and inline secrets. Failed batch steps additionally run `redactExactValues` on structured step errors so literals taken from that step’s argv (cookie value, storage set value, `--password` / `--password=` tokens) cannot reappear inside formatted error blobs. When the full batch is large enough to need its own aggregate spill, that spill reapplies these per-command data and argv redactors before persistence rather than using generic batch redaction.
|
|
791
814
|
|
|
792
|
-
`nextActions` is an optional machine-readable list of exact native `agent_browser` follow-ups. Each entry includes `tool: "agent_browser"`, an `id`, a short `reason`, optional `safety`, and either `params` (`args`, optional `stdin`, optional `sessionMode`, optional `networkSourceLookup`, optional `electron`) or an `artifactPath` for saved-file workflows. Failure prose mirrors up to six payloads so Pi models can execute them without access to structured `details`; stdin up to 500 characters is shown exactly after redaction, while longer stdin stays `details.nextActions`-only to bound context. Agents should prefer the visible or structured payload over guessed commands. Browser-bearing follow-ups preserve a known `details.sessionName` with `--session <name>` so retries and diagnostics cannot drift into the implicit session, except actions whose `params.sessionMode` is `"fresh"`, which deliberately stay unprefixed because the planner ignores `sessionMode` alongside an explicit `--session`; when a result also ran under an upstream namespace, follow-up `params.args` preserve its exact value, including explicit `--namespace ""`, so an ambient namespace cannot redirect the same daemon/restore-state identity. Tab/session recovery id strings are centralized in `AGENT_BROWSER_RECOVERY_NEXT_ACTION_IDS`, while rich-input focus/click recovery ids are centralized in `AGENT_BROWSER_RICH_INPUT_RECOVERY_NEXT_ACTION_IDS` plus `getAgentBrowserRichInputRecoveryNextActionId(s)` in `extensions/agent-browser/lib/results/recovery-actions.ts`; docs and tests mirror those registries/helpers rather than inventing recovery ids in prose. Current recommendations include: ordinary `timeout` failures → `inspect-after-timeout` (`snapshot -i`), with `wait --text` using the more specific `inspect-after-text-assertion-failure`, and `wait --url` (including compiled `job.assertUrl`) also appending `fresh-session-after-url-wait-timeout` (`sessionMode: "fresh"` + `open about:blank`, ranked after the inspect action) with guidance that a silently missed upstream click dispatch may have prevented the expected navigation and that the fresh session should replay the flow as one batch; navigation-shaped `upstream-error` failures → `inspect-page-after-navigation-error`; direct, `semanticAction`, raw `find` (including `nth` and omitted default-click), or failed `batch`/`job` click actions whose presented upstream error contains both `is covered by` and `at its click point` → session-aware `inspect-overlay-state` (`snapshot -i`) while retaining `failureCategory: "upstream-error"`, with no blind retry of the blocked click and no guessed dismiss action (error text may come from `error`, a string `data`, or `data.error` in a failed envelope and remains available as `error` in caller-requested `--json`; empty/null outer errors do not hide data errors or failed batch rows); failed script-session cleanup → exact `close-script-session-after-cleanup-failure`; timed-out jobs/batches with a retryable read-only/idempotent first incomplete step → `retry-timeout-step`, while timed-out flows whose first incomplete step may be mutating → `inspect-current-page-after-timeout` (`snapshot -i`) before splitting the remaining work into shorter batches; raw `connect` success → session-scoped `verify-connected-session-url` (`get url`) plus `list-connected-session-tabs`; page-content reads remain blocked until the current target is verified with `get url`, after which the agent can select/confirm a stable `tab t<N>`, verify it with `get url`, and run `snapshot -i`; `snapshot` failures whose upstream error says `No active page` and whose wrapper result has a known session → `list-tabs-after-no-active-page` only, because this path has no wrapper-observed safe tab id to select atomically; browser profile/user-data-dir resolution failures → `inspect-browser-profiles` (`profiles`) and `run-agent-browser-doctor` (`doctor`) before retrying opens; Electron launches → wrapper-tracked `electron.status` / `electron.probe` / `electron.cleanup` actions plus session-scoped tab/snapshot inspection when attached; Electron status/probe mismatch diagnostics → `reattach-electron-launch` plus fresh tab/snapshot inspection; Electron post-command health failures → status/probe/cleanup for the same `launchId`; Electron or contenteditable fill verification mismatches → `inspect-after-fill-verification` and `verify-filled-value`; Electron same-URL ref freshness warnings → `refresh-electron-refs-after-rerender`; packaged-Electron `sourceLookup` no-candidate diagnostics → session snapshot, launch probe, and tab list; Electron cleanup partial failures → status plus retry-cleanup for the same wrapper-owned `launchId`; `open` success → `snapshot -i`; mutating/navigation commands (see `buildAgentBrowserNextActions` in source for the exact command set) → `snapshot -i`; stale refs and selector failures → `snapshot -i` via `refresh-interactive-refs` (prefixed with `--session <name>` when the failed call ran in a named or managed session); selector misses with exact current snapshot role/name matches → direct ref retries via `try-current-visible-ref` or bounded `try-current-visible-ref-N` for non-fill targets; semantic `fill` selector misses with exact current editable refs → `focus-current-editable-ref` / `click-current-editable-ref` or numbered variants that do not include fill text or submit; unknown getter shortcuts such as `title` / `url` → exact read-only retries like `get title` / `get url` with ids `use-get-title` / `use-get-url`; compact `network requests` results with safe request IDs → bounded read-only request detail, `networkSourceLookup`, path filter, or HAR-capture follow-ups; semantic `selector-not-found` failures that compiled from `semanticAction` may append `try-button-name-candidate` or `try-link-name-candidate` after presentation `nextActions` only for the bounded click pair enumerated under `semanticAction`; semantic `stale-ref` failures that compiled from `semanticAction` `find` argv may also include `retry-semantic-action-after-stale-ref` after that snapshot step; successful snapshots or qualifying same-URL non-Electron top-level clicks (see `overlayBlockers` below) with snapshot evidence of likely overlay/banner/dialog close controls may append `inspect-overlay-state` and bounded `try-overlay-blocker-candidate-*` entries; successful top-level `scroll` calls whose pre/post viewport and sampled scroll-container positions do not change may append `inspect-after-noop-scroll` and `verify-noop-scroll-visually`; explicit combobox-targeted actions that focus a combobox without visible options may append `inspect-focused-combobox`, `try-open-combobox-with-arrow`, and `try-open-combobox-with-enter`; `get text <selector>` calls with hidden/multiple CSS matches may append `inspect-visible-text-candidates` with a read-only `eval --stdin` probe (each prefixed with `--session <name>` when `details.sessionName` is set, same `sessionPrefixArgs` rule as other session-scoped follow-ups); confirmations → exact `confirm <id>` and `deny <id>` choices; generic tab drift → `list-tabs-for-recovery` with `tab list` first, then select or confirm the stable target before running `snapshot -i`; about:blank or tab-drift recovery with a wrapper-known target → `list-tabs-for-about-blank-recovery` or `list-tabs-for-tab-drift-recovery`, plus `select-intended-tab-after-drift` and `snapshot-after-tab-recovery` when the wrapper already observed the stable `t<N>` tab id; `wait --text` assertion failures → `inspect-after-text-assertion-failure` with a read-only snapshot; download verification failures or missing successful download artifacts → `wait --download [path]`; saved artifacts → the artifact path to inspect/consume after checking `artifactVerification`/metadata; missing non-download artifacts → `verify-artifact-path` so agents do not trust an absent file. When nothing applies, the field is omitted.
|
|
815
|
+
`nextActions` is an optional machine-readable list of exact native `agent_browser` follow-ups. Each entry includes `tool: "agent_browser"`, an `id`, a short `reason`, optional `safety`, and either `params` (`args`, optional `stdin`, optional `sessionMode`, optional `networkSourceLookup`, optional `electron`) or an `artifactPath` for saved-file workflows. Failure prose mirrors up to six payloads so Pi models can execute them without access to structured `details`; stdin up to 500 characters is shown exactly after redaction, while longer stdin stays `details.nextActions`-only to bound context. Agents should prefer the visible or structured payload over guessed commands. Browser-bearing follow-ups preserve a known `details.sessionName` with `--session <name>` so retries and diagnostics cannot drift into the implicit session, except actions whose `params.sessionMode` is `"fresh"`, which deliberately stay unprefixed because the planner ignores `sessionMode` alongside an explicit `--session`; when a result also ran under an upstream namespace, follow-up `params.args` preserve its exact value, including explicit `--namespace ""`, so an ambient namespace cannot redirect the same daemon/restore-state identity. Tab/session recovery id strings are centralized in `AGENT_BROWSER_RECOVERY_NEXT_ACTION_IDS`, while rich-input focus/click recovery ids are centralized in `AGENT_BROWSER_RICH_INPUT_RECOVERY_NEXT_ACTION_IDS` plus `getAgentBrowserRichInputRecoveryNextActionId(s)` in `extensions/agent-browser/lib/results/recovery-actions.ts`; docs and tests mirror those registries/helpers rather than inventing recovery ids in prose. Current recommendations include: `session info` timeouts → `retry-session-info` (`session info` in the exact session/namespace); ordinary browser `timeout` failures → `inspect-after-timeout` (`snapshot -i`), with `wait --text` using the more specific `inspect-after-text-assertion-failure`, and `wait --url` (including compiled `job.assertUrl`) also appending `fresh-session-after-url-wait-timeout` (`sessionMode: "fresh"` + `open about:blank`, ranked after the inspect action) with guidance that a silently missed upstream click dispatch may have prevented the expected navigation and that the fresh session should replay the flow as one batch; navigation-shaped `upstream-error` failures → `inspect-page-after-navigation-error`; direct, `semanticAction`, raw `find` (including `nth` and omitted default-click), or failed `batch`/`job` click actions whose presented upstream error contains both `is covered by` and `at its click point` → session-aware `inspect-overlay-state` (`snapshot -i`) while retaining `failureCategory: "upstream-error"`, with no blind retry of the blocked click and no guessed dismiss action (error text may come from `error`, a string `data`, or `data.error` in a failed envelope and remains available as `error` in caller-requested `--json`; empty/null outer errors do not hide data errors or failed batch rows); failed script-session cleanup → exact `close-script-session-after-cleanup-failure`; timed-out jobs/batches with a retryable read-only/idempotent first incomplete step → `retry-timeout-step`, while timed-out flows whose first incomplete step may be mutating → `inspect-current-page-after-timeout` (`snapshot -i`) before splitting the remaining work into shorter batches; raw `connect` success → session-scoped `verify-connected-session-url` (`get url`) plus `list-connected-session-tabs`; page-content reads remain blocked until the current target is verified with `get url`, after which the agent can select/confirm a stable `tab t<N>`, verify it with `get url`, and run `snapshot -i`; `snapshot` failures whose upstream error says `No active page` and whose wrapper result has a known session → `list-tabs-after-no-active-page` only, because this path has no wrapper-observed safe tab id to select atomically; browser profile/user-data-dir resolution failures → `inspect-browser-profiles` (`profiles`) and `run-agent-browser-doctor` (`doctor`) before retrying opens; Electron launches → wrapper-tracked `electron.status` / `electron.probe` / `electron.cleanup` actions plus session-scoped tab/snapshot inspection when attached; Electron status/probe mismatch diagnostics → `reattach-electron-launch` plus fresh tab/snapshot inspection; Electron post-command health failures → status/probe/cleanup for the same `launchId`; Electron or contenteditable fill verification mismatches → `inspect-after-fill-verification` and `verify-filled-value`; Electron same-URL ref freshness warnings → `refresh-electron-refs-after-rerender`; packaged-Electron `sourceLookup` no-candidate diagnostics → session snapshot, launch probe, and tab list; Electron cleanup partial failures → status plus retry-cleanup for the same wrapper-owned `launchId`; `open` success → `snapshot -i`; mutating/navigation commands (see `buildAgentBrowserNextActions` in source for the exact command set) → `snapshot -i`; stale refs and selector failures → `snapshot -i` via `refresh-interactive-refs` (prefixed with `--session <name>` when the failed call ran in a named or managed session); selector misses with exact current snapshot role/name matches → direct ref retries via `try-current-visible-ref` or bounded `try-current-visible-ref-N` for non-fill targets; semantic `fill` selector misses with exact current editable refs → `focus-current-editable-ref` / `click-current-editable-ref` or numbered variants that do not include fill text or submit; unknown getter shortcuts such as `title` / `url` → exact read-only retries like `get title` / `get url` with ids `use-get-title` / `use-get-url`; compact `network requests` results with safe request IDs → bounded read-only request detail, `networkSourceLookup`, path filter, or HAR-capture follow-ups; semantic `selector-not-found` failures that compiled from `semanticAction` may append `try-button-name-candidate` or `try-link-name-candidate` after presentation `nextActions` only for the bounded click pair enumerated under `semanticAction`; semantic `stale-ref` failures that compiled from `semanticAction` `find` argv may also include `retry-semantic-action-after-stale-ref` after that snapshot step; successful snapshots or qualifying same-URL non-Electron top-level clicks (see `overlayBlockers` below) with snapshot evidence of likely overlay/banner/dialog close controls may append `inspect-overlay-state` and bounded `try-overlay-blocker-candidate-*` entries; successful top-level `scroll` calls whose pre/post viewport and sampled scroll-container positions do not change may append `inspect-after-noop-scroll` and `verify-noop-scroll-visually`; explicit combobox-targeted actions that focus a combobox without visible options may append `inspect-focused-combobox`, `try-open-combobox-with-arrow`, and `try-open-combobox-with-enter`; `get text <selector>` calls with hidden/multiple CSS matches may append `inspect-visible-text-candidates` with a read-only `eval --stdin` probe (each prefixed with `--session <name>` when `details.sessionName` is set, same `sessionPrefixArgs` rule as other session-scoped follow-ups); confirmations → exact `confirm <id>` and `deny <id>` choices; generic tab drift → `list-tabs-for-recovery` with `tab list` first, then select or confirm the stable target before running `snapshot -i`; about:blank or tab-drift recovery with a wrapper-known target → `list-tabs-for-about-blank-recovery` or `list-tabs-for-tab-drift-recovery`, plus `select-intended-tab-after-drift` and `snapshot-after-tab-recovery` when the wrapper already observed the stable `t<N>` tab id; `wait --text` assertion failures → `inspect-after-text-assertion-failure` with a read-only snapshot; download verification failures or missing successful download artifacts → `wait --download [path]`; saved artifacts → the artifact path to inspect/consume after checking `artifactVerification`/metadata; missing non-download artifacts → `verify-artifact-path` so agents do not trust an absent file. When nothing applies, the field is omitted.
|
|
793
816
|
|
|
794
817
|
**Unknown-command getter hints (failure presentation):** `buildErrorPresentation` in `extensions/agent-browser/lib/results/presentation/errors.ts` only runs this path when upstream error text (after model-facing redaction) matches `unknown command`, `unknown subcommand`, or `unrecognized command` (case-insensitive) **and** the failed invocation’s primary command token is one of `attr`, `count`, `html`, `text`, `title`, `url`, or `value`. Visible text then includes a grouped-`get` hint line plus per-token guidance (`get text <selector>`, `get html …`, `get attr …`, `get count …`, `get value …`, `get title`, `get url`). Machine `nextActions` with ids `use-get-title` / `use-get-url` are emitted only for `title` / `url`, with `params.args` optionally prefixed by `--session <name>` when the failed call targeted a named session. If the error string already contains `Agent-browser hint:` from selector recovery (stale-ref or unsupported selector dialect appendages), the getter block is skipped so two stacked `Agent-browser hint:` headers are not emitted.
|
|
795
818
|
|
|
@@ -884,7 +907,7 @@ Additional structured fields can appear when relevant:
|
|
|
884
907
|
- `promptGuard` when the requested-artifact-before-close guard blocks browser close before required prompt artifact paths are verified; implementation lives in `extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts`
|
|
885
908
|
- `overlayBlockers` for conservative overlay/banner/dialog blocker candidates when a successful snapshot itself contains strong modal evidence, or after a qualifying top-level `@e…` / `ref=` click stays on the same URL, no `clickDispatch` diagnostic fired, and a fresh snapshot provides evidence (`candidates`, `summary`, and `snapshot` per `OverlayBlockerDiagnostic` in `extensions/agent-browser/index.ts`). CSS selector clicks do not run this overlay probe.
|
|
886
909
|
- `visibleRefFallback` after a raw `find` or compiled `semanticAction` fails with `selector-not-found` and a fresh snapshot finds exact role/name `@ref` matches. Shape follows `VisibleRefFallbackDiagnostic` in `extensions/agent-browser/lib/results/selector-recovery.ts`: `{ candidates, snapshot, summary, target }`, where each candidate has `ref`, `role`, `name`, optional direct ref `args`, and `reason`; visible text appends `Current snapshot ref fallback`. Non-fill candidates with direct args add `try-current-visible-ref` or numbered `try-current-visible-ref-N` actions. Fill candidates omit direct args and target text so recovery details do not repeat potentially sensitive fill text.
|
|
887
|
-
- `refSnapshotInvalidation` after a confirmed cold managed-session shutdown (`reason: "page-transition"`, including when reopening fails), a session `snapshot` fails with `No active page`, any upstream-executed `record start` attempt
|
|
910
|
+
- `refSnapshotInvalidation` after a confirmed cold managed-session shutdown (`reason: "page-transition"`, including when reopening fails), a session `snapshot` fails with `No active page`, any upstream-executed `record start` attempt or URL-bearing `record restart` conservatively invalidates refs (including failures, for older-native protection rather than proof of a page change), a direct or reached batch `window new` / `diff url` attempt changes the page, or a failed non-batch transition command (`eval`, `back`, `forward`, `reload`, `connect`, `state load`, `tab` selection) whose live URL re-verification probe observed the page (a failed transition can still have mutated the document, so the verified URL is kept but the prior refs are not). Shape follows `SessionRefSnapshotInvalidation` in `extensions/agent-browser/lib/session-page-state.ts`: `{ reason: "no-active-page" | "page-transition", summary }`; replay preserves the persisted summary. The wrapper deletes prior refs for that session, persists the invalidation for resume, and blocks mutation-prone `@e…` preflight with `failureCategory: "stale-ref"` until a successful fresh `snapshot -i` records refs again.
|
|
888
911
|
- `snapshotFilter` after wrapper-side `snapshot -i --search <text>` or `snapshot -i --filter role=<role>`. Shape: `{ cleanArgs, search?, role?, matchedRefs, totalRefs, visibleLines, totalLines, renderedTextMatches?, renderedTextTotalMatches?, renderedTextTruncated? }`. Search runs one bounded read-only rendered-DOM probe across the full document; each visible match carries bounded `text`, `tagName`, `kind` (`text` or prioritized `validation`), `offscreen`, optional `role`/accessible `name`, and a unique mapped `ref` when the full snapshot supports it. Hidden elements are excluded. The filtered accessibility snapshot remains separate, while `details.refSnapshot` still records the full upstream ref map for later stale-ref checks.
|
|
889
912
|
- `snapshotViewport` after wrapper-side `snapshot --viewport` (with or without `-i`, `--search`, or `--filter`). Shape matches the scroll-position probe: viewport scroll offsets, inner/document dimensions, sampled scrollable-container count, and bounded container offsets. The wrapper strips `--viewport` before upstream spawn and gathers this with a read-only `eval --stdin` call.
|
|
890
913
|
- `snapshotDiff` after wrapper-side `snapshot --diff` (with or without `-i`, `--search`, `--filter`, or `--viewport`). Shape: `{ addedRefs, removedRefs, changedRefs, unchangedRefs, summary }`, comparing ref ids plus role/name metadata from the previous wrapper-tracked snapshot for the session with the newly returned full ref map. It is a quick ref-map delta, not a visual diff.
|
|
@@ -894,24 +917,24 @@ Additional structured fields can appear when relevant:
|
|
|
894
917
|
- `scrollPage` when the wrapper moves `document.scrollingElement` directly for `scroll <up|down|left|right> [px|percent]` or `scroll to end|top`; it temporarily disables smooth scrolling so immediate before/after offsets are reliable, returns `{ request, result }`, and includes `exitCode: 0` on success. Directional document no-movement falls through to upstream wheel behavior so nested panes still work. Explicit CSS-container calls `scroll <selector> <up|down|left|right> [px|percent]` remain wrapper-handled and report `details.scrollContainer`. All scroll helper shims are skipped when startup-scoped flags are present so the requested browser/profile launches before any helper command.
|
|
895
918
|
- `scrollNoop` after a nominally successful large **top-level** upstream scroll fallback on an existing or fresh managed session when wrapper-side read-only probes before and after the command show no change in `window.scrollX` / `window.scrollY` and no change in the sampled prominent scrollable containers. The wrapper reclassifies this outcome as `failureCategory: "upstream-error"` rather than claiming the page scrolled. To avoid pre-launching a session without caller startup state, this probe is skipped for small pixel scrolls, calls that would create a managed session only for the probe, and invocations with startup-scoped flags such as `--profile`, `--state`, `--restore`, `--namespace`, `--session-name`, `--cdp`, providers, init scripts, or similar launch settings. Shape: `{ reason: "no-observed-scroll-position-change", message, before, after, recommendations }`; `before` / `after` include viewport dimensions, document scroll dimensions, and up to ten sampled container descriptors plus scroll offsets. Container descriptors use only sample index, tag name, and ARIA role; DOM ids/classes are intentionally not stored. This diagnostic is conservative evidence that the page-level scroll likely missed a nested pane, not proof that every app-specific region is unchanged. Visible text starts with `Scroll completed with no observed movement`, appends `Scroll diagnostic: no observed scroll movement`, sets `details.data.scrolled` to `false` / `details.data.noMovement` to `true`, and `details.nextActions` gains `inspect-after-noop-scroll` (`snapshot -i`) plus `verify-noop-scroll-visually` (`screenshot`), session-prefixed when applicable.
|
|
896
919
|
- `comboboxFocus` after a successful explicit combobox-targeted `click` / `fill` / `find … click|fill` (for example `semanticAction` with role `combobox`, including when that semantic action resolves through a current visible `@ref` before execution) when a read-only probe sees the active element is combobox-like, `aria-expanded` is explicitly present (`false` or `true`), and no visible `listbox` / `option` / menu option elements are open. Shape: `{ reason: "focused-combobox-without-visible-options", message, activeElement, visibleListboxCount, visibleOptionCount, recommendations }`; `activeElement` includes bounded role/tag/expanded/hasPopup/name metadata with normal text redaction. Visible text appends `Combobox diagnostic: focused combobox did not expose visible options`, and `details.nextActions` gains `inspect-focused-combobox` (`snapshot -i`), `try-open-combobox-with-arrow` (`press ArrowDown`), and `try-open-combobox-with-enter` (`press Enter`), session-prefixed when applicable. The diagnostic is deliberately gated to explicit combobox-targeted calls to avoid extra probes or false positives on ordinary clicks/textboxes.
|
|
897
|
-
- `recordingDependencyWarning` after a successful `record start` or `record restart` when the wrapper cannot find an executable `ffmpeg` on the Pi process `PATH`. Shape: `{ reason: "ffmpeg-missing-for-recording", dependency: "ffmpeg", command, message, recommendations }`. Visible text appends `Recording dependency warning: ffmpeg not found on PATH`. This is a non-blocking preflight
|
|
920
|
+
- `recordingDependencyWarning` after a successful `record start` or `record restart` when the wrapper cannot find an executable `ffmpeg` on the Pi process `PATH`. Shape: `{ reason: "ffmpeg-missing-for-recording", dependency: "ffmpeg", command, message, recommendations }`. Visible text appends `Recording dependency warning: ffmpeg not found on PATH`. This is a non-blocking warning after native success, not a preflight or encoding check. Native 0.37 validates ffmpeg at startup; older supported natives may defer failure. Treat the pending output as unverified, stop and check its result, then install ffmpeg before starting a new recording.
|
|
898
921
|
- `selectorTextVisibility` after a **successful** upstream `get text <selector>` (standalone or inside a successful `batch`) when the wrapper’s follow-up probe finds a hazard: more than one DOM match (upstream reads the first `querySelectorAll` hit, which may be the wrong tab/panel), or the first match is hidden while at least one other match is visible (requires multiple DOM nodes so a visible peer exists; a lone hidden match is not flagged). The probe is a read-only `eval --stdin` script (`buildVisibleTextProbeScript` in `extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts`) that counts matches, applies a small visibility heuristic (`display`/`visibility`/`opacity` plus non-zero client rects), may include a redacted `firstVisibleTextPreview`, and may include up to eight `visibleCandidates` entries (`index` in `querySelectorAll`, `tagName`, optional `role`, optional redacted `textPreview`). It is **not** run for simple id selectors, page-scoped `@e…` selectors, or when the selector string is withheld because `selectorMayExposeSensitiveLiteral` would risk echoing secrets in probe output. `details.selectorTextVisibility` mirrors the primary diagnostic (first sorted entry); when several selectors in one `batch` qualify, `selectorTextVisibilityAll` lists every diagnostic sorted so hidden-first cases precede generic multi-match ambiguity. Appended visible warning text names the matching `details.nextActions` id and may list visible candidate previews. Appended `details.nextActions` use ids `inspect-visible-text-candidates` and `inspect-visible-text-candidates-2`, … with the probe replayed via `eval --stdin` for each hazardous selector. If the probe still leaves more than one visible candidate, it is only ambiguity evidence; agents should narrow the selector, use a current visible `@ref`, or run a targeted visible-element `eval --stdin` rather than trusting the broad selector.
|
|
899
922
|
- `electronGetTextScopeWarning` after a successful wrapper-tracked attached Electron `get text <selector>` (standalone or successful `batch`) when a broad non-ref CSS selector such as `body`, `html`, `main`, `div`, or `[role=application]` may read the whole app shell. Ordinary browser pages do not qualify without wrapper-owned Electron launch provenance. Shape: `{ selector, summary, electronContext: { launchId?, sessionName?, url? } }`; multiple batched diagnostics use `electronGetTextScopeWarnings`. Visible text appends `Broad Electron get text selector warning`, and next actions use `snapshot-for-electron-text-scope` ids with session-scoped `snapshot -i` payloads.
|
|
900
923
|
- `evalStdinHint` after a successful `eval --stdin` when caller stdin (trimmed) looks function-shaped to the wrapper’s lightweight detector (in `extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts`: leading `function` / `async function`, parenthesized arrow `(…) =>`, or a concise `name =>` / `async name =>` form) **and** upstream JSON `data` is an object whose `result` field is a plain empty object (`{}`). Arrays such as `[]` do not qualify. It includes `reason` and `suggestion`; visible output appends `Eval stdin hint` with the same guidance. This is a heuristic for the common mistake of returning a function object instead of invoking it or passing a plain expression, not a JavaScript parser or proof that the page returned no useful data. Before this diagnostic path runs, the wrapper also recovers the common malformed native-tool call `args: ["eval", "--stdin", "..."]` with no top-level `stdin` by moving trailing `args` tokens after `--stdin` into the process stdin stream.
|
|
901
924
|
- `evalResultWarning` after a successful `eval --stdin` when the current or prior page URL is `file:` (from navigation summary, session tab target, or persisted session page state), upstream JSON `data.result` is strictly `null`, and stdin is non-empty and not a trivial literal `null`/`undefined`. Fields: `reason`, `suggestion`. Visible output appends `Eval result warning` without failing the tool. Use snapshot -i, ref-based getters, screenshots, or http(s) fixtures when file:// null results are inconclusive.
|
|
902
|
-
- `timeoutPartialProgress` after `runAgentBrowserProcess` reports `timedOut` (wrapper child-process watchdog) when best-effort recovery finds useful context. `summary` is a short sentence counting recovered planned-step state and declared artifact paths, plus whether page context came from live session reads or only from a planned URL (when nothing in the plan declares an artifact path, the fraction may read `0/0` while `currentPage` can still carry session or planned URL context). `steps` lists planned argv from the compiled `job` or `qa` batch plan (`compiledJob` in `extensions/agent-browser/index.ts`, which is only populated for those top-level modes) or, when that object is absent, from the
|
|
925
|
+
- `timeoutPartialProgress` after `runAgentBrowserProcess` reports `timedOut` (wrapper child-process watchdog) when best-effort recovery finds useful context. `summary` is a short sentence counting recovered planned-step state and declared artifact paths, plus whether page context came from live session reads or only from a planned URL (when nothing in the plan declares an artifact path, the fraction may read `0/0` while `currentPage` can still carry session or planned URL context). `steps` lists planned argv from the compiled `job` or `qa` batch plan (`compiledJob` in `extensions/agent-browser/index.ts`, which is only populated for those top-level modes) or, when that object is absent, from the effective upstream `batch` source: raw argument command strings exclusively when present, otherwise JSON-array stdin, whether caller-authored or wrapper-generated for `sourceLookup` / `networkSourceLookup` (1-based indices). Ignored stdin does not contribute recovery steps or artifact evidence. Generated rows such as `open.loadState` waits may include `generatedFrom`. Each step includes `status` (`completed`, `failed`, `pending`, or `unknown`) and optional `reason`; the first incomplete step becomes `retryStep`, but `retry` and top-level `retry-timeout-step` are emitted only for read-only or idempotent commands such as waits, snapshots, screenshots, navigation, and diagnostics. Each retry uses `args: ["batch"]` with `stdin` containing the one original row, preserving native row operands instead of reinterpreting them as outer CLI globals. Mutating steps such as clicks, fills, keyboard typing, presses, selects, or checks are still identified as the first incomplete step but omit executable retry args because they may already have run; when the timed-out session is still usable and its target is already verified, `details.nextActions` can instead include `inspect-current-page-after-timeout` (`snapshot -i`) so the agent verifies current state before continuing with a shorter split flow. When the target is unknown, every standalone snapshot action is removed and replaced by one executable `verify-page-target-after-timeout` action: a session-scoped `batch --bail` with stdin `[["get","url"],["snapshot","-i"]]`, so snapshot runs only after the wrapper's page-target guard is satisfied; visible failure text prints those redacted args and short stdin rather than pointing only to structured details. Dialog `status`, `accept`, and `dismiss` remain allowed while the target is unknown so timeout dialog recovery actions are executable. When a retryable step timed out during `sessionMode: "fresh"` and no live URL was recovered, `retry-timeout-step` uses top-level `sessionMode: "fresh"` instead of prefixing the abandoned generated session name. `currentPage` comes from session-scoped `get url` followed by `get title` when the session answers, otherwise a fallback URL may be inferred from the last `open` / `navigate` / `pushstate` step in the plan; `liveUrlRecovered` is true only when the wrapper recovered a live URL, so planned URLs are not treated as proof that the page actually opened. `openedButPostOpenTimedOut` is set when a live opened page was recovered and a later step appears to have timed out. `artifacts` covers declared output paths on `screenshot`, `pdf`, `download`, and `wait --download` steps (absolute path, existence, `state`, optional `sizeBytes`, `stepIndex`). It uses native operand positions, including literal dash-leading paths: first operand for `pdf`, second for `download`, and the next retained operand after the first timeout pair is removed for `wait --download` / `-d`. Visible text repeats the same block under `Timeout partial progress`, applying URL and path-segment redaction; the prose `Planned steps` list shows at most six steps, then an omitted-count line when the plan is longer. This is recovery evidence only; missing entries do not prove the upstream step never ran or that no other side effects occurred.
|
|
903
926
|
- `managedSessionHeadedAutosaveInterval` on active/current-after-failure wrapper-owned headed session rows, containing the canonical effective launch-time `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` string (invalid or out-of-range explicit values resolve to upstream's `"30000"` default). It is `"0"` for the wrapper default and can hold an explicit interval such as `"1000"`; transcript replay and still-owned off-current helpers reapply the recorded value. It is omitted for sessionless, headless, caller-owned, abandoned, and closed calls. If a resumed Pi process explicitly requests a different value in either direction, non-close calls fail with close-plus-fresh recovery guidance while close still uses the recorded daemon value.
|
|
904
927
|
- `managedSessionHeadedAutosaveDisabled: true` is the narrower compatibility marker that the targeted session uses the wrapper's default interval `0`, rather than an explicit caller interval. It accompanies `managedSessionHeadedAutosaveInterval: "0"` on active rows and remains omitted for explicitly configured autosave.
|
|
905
928
|
- `managedSessionOutcome` after a managed-session plan reaches process execution (`buildManagedSessionOutcome` / `formatManagedSessionOutcomeText` in `extensions/agent-browser/lib/orchestration/browser-run/session-state.ts`). Populated when `buildExecutionPlan` injects an extension-managed implicit or fresh `--session`, and also when a successful explicit `--session <current-wrapper-managed-session> close` closes the current managed session. It remains omitted for unrelated explicit user-managed sessions and for sessionless inspection/local paths that skip injection. Successful nested-batch lifecycle rows are evaluated in order: a terminal close reports and replays `status: "closed"` even when aggregate artifact verification makes the tool result fail; a later lifecycle-proven browser launch (including a post-close `record stop`) keeps the session active, an explicitly non-launching diagnostic leaves it closed, and an unknown row stays conservatively active even when the failed batch was the first managed call. Fields: `status` (`created`, `replaced`, `unchanged`, `closed`, `preserved`, or `abandoned`), `sessionMode`, `attemptedSessionName`, `previousSessionName`, `currentSessionName`, optional `currentSessionNamespace`, optional `replacedSessionName`, optional `replacedSessionNamespace`, optional `replacedSessionClosed` (false means automatic close failed and the previous session remains wrapper-owned/restorable for explicit cleanup), `activeBefore`, `activeAfter`, `succeeded`, and `summary` (machine-oriented; may include generated session names). Use `currentSessionNamespace` with `currentSessionName` when following preserved-session recovery actions; retry-fresh actions stay in the attempted namespace. Model-visible echo: when `sessionMode` is `"fresh"` **and** `succeeded` is false, or when `replacedSessionClosed` is false after a replacement, the wrapper appends action-oriented `Managed session outcome` and `Recovery` lines without repeating generated session ids in visible prose; session names remain in `details.managedSessionOutcome`. Failed fresh launches may also append `details.nextActions` such as `run-agent-browser-doctor`, `verify-current-managed-session`, `snapshot-current-managed-session`, or `retry-fresh-managed-session`. When other trailing diagnostic prose is also emitted in the same result, that block is concatenated **after** semantic-action candidate lines, overlay/selector-visibility tails, eval hints/warnings, and `Timeout partial progress` (see `rawAppendedDiagnosticText` in `extensions/agent-browser/lib/orchestration/browser-run/final-result.ts`). For `"auto"` failures the same struct may appear on `details` without that extra line. When post-upstream analysis (for example **`qa`** preset failure) flips the overall tool result after a successful batch, or a fresh `job`/batch opens the requested page and then a later step fails, the managed-session transition still reflects that the fresh browser became current. The visible recovery says the fresh launch became current and points to `failureCategory` / `qaPreset` / `batchFailure` for the post-launch failure instead of telling the agent that the old session was preserved.
|
|
906
929
|
- `imagePath` / `imagePaths` for Pi inline image attachments from the **`screenshot`** command (including batched screenshot steps). **`diff screenshot`** still records the diff output as an `image`-kind entry in `details.artifacts`, but it does **not** populate `imagePath` / `imagePaths` or attach an inline image: only plain `screenshot` is treated as a trusted live-capture path for automatic inlining (`isTrustedScreenshotOutput` in `extensions/agent-browser/lib/results/presentation/artifacts.ts`).
|
|
907
|
-
- `artifacts` for saved files such as screenshots, `state save` outputs, `diff screenshot` diff images, PDFs, downloads, `wait --download` / `wait -d` files, traces, CPU profiles, completed
|
|
930
|
+
- `artifacts` for saved files such as screenshots, `state save` outputs, `diff screenshot` diff images, PDFs, downloads, `wait --download` / `wait -d` files, traces, CPU profiles, completed video recordings, path-bearing HAR captures, and future recording output paths reported by `record start` / `record restart`. Non-file URL payloads such as `data:` / `blob:` / `http(s):` values are not treated as verified local artifacts. For direct artifact commands and batch artifact steps, the wrapper creates parent directories for requested paths before spawning upstream. Filesystem `mkdir` failures at this shared preparation boundary return `validation-error`, `agentBrowserStarted: false`, the attempted directory and `verify-artifact-path` guidance. Raw batch strings are never rewritten; use absolute artifact paths because the daemon's cwd may differ from Pi's. Each artifact includes the original saved or requested `path`, resolved `absolutePath`, `kind`/`artifactType`, optional `mediaType`, optional `extension`, best-effort disk metadata such as `exists`, `sizeBytes`, and `updatedAtMs`, plus `requestedPath`, `status`, `cwd`, `session`, `namespace`, and `tempPath` when applicable. `requestedPath` is retained only when known from the caller, separately from reported/resolved locations; a differing screenshot report remains in `tempPath` and is displayed as `Reported path`, whether it is a temporary file or a canonical path alias. Ordinary file `mediaType` values come from bounded PNG/JPEG/GIF/WebP header recognition, not suffixes; unknown, missing, unreadable or truncated headers leave it undefined. Header recognition is not full-file format validation. Inline screenshot attachments use the same byte classifier and existing size limit. Direct-anchor downloads retain their response Content-Type metadata. For commands that create/update artifacts, a path that existed but was not updated during this command uses `status: "stale"`; observational `wait --download` may accept a file completed just before the wait began. Pending `record start` / `record restart` artifacts use `status: "pending"`, omit `exists` rather than reporting false, and include `recordingState: "openRecording"` / `willExistOnStop: true`. Within one Pi extension process, the wrapper keeps an unbounded transcript-backed active-recording reservation index separate from the bounded artifact manifest, keyed by canonical namespace plus session; still-live process-owned reservations survive branch switches, while known closures are appended after tree navigation and during shutdown/reload so a close on one branch cannot be resurrected after returning to an older branch. Persisted active reservations require absolute storage paths and cwd; their display paths may remain relative. If a journal append fails, the next serialized browser boundary, tree navigation, or shutdown retries all current reservations and known closures. `recordingPersistenceWarning` and visible warning text remain present while restart protection is not durable; successful recovery is quiet and cleanup still runs. Artifact lifecycle calls, explicit `wait --download <path>` / `wait -d <path>` destinations, and result `outputPath` writes serialize around the global destination check/update, every successful direct, ordered nested-batch, fresh-replacement, script, Electron, or shutdown close retires only its exact identity at that lifecycle point, and destination reuse is rejected through lexical, existing or dangling symlink, hardlink, full Unicode-fold, or macOS/Windows case aliases. Batch preflight rejects `record start` / `record restart` after a close row because upstream can report a recording that did not start; split those operations into separate calls. A `No recording in progress` stop failure, direct or nested, first checks the matching native receipt once. Retirement preserves the receipt and freshly checked file metadata; it never turns an existing file into a missing file merely because no recording is active. A later successful batch recording row opens its new pending path normally. Batch preflight applies the same distinct-destination rule to the steps upstream will execute: raw argument command strings exclusively when any exist, stdin arrays only otherwise; upstream-ignored stdin rows cannot fail artifact preflight, add pending recordings, or create parent directories. Parent directories are prepared for the effective steps in both modes; raw argument strings are never rewritten, so the screenshot absolute-path normalization and tracked path request apply to stdin rows only. Outer CLI globals are removed before artifact parsing, but native batch row operands stay literal: `pdf --quick ignored.pdf` targets `--quick`, not `ignored.pdf`. Reservation checks, preparation, and requested-path presentation follow that same distinction. Recording path/URL consumers skip complete numeric `--fps` pairs without rewriting argv; native still validates rate, format and extra arguments. FPS-only calls keep the intended pinned tab.
|
|
908
931
|
|
|
909
932
|
Recording destinations are reserved within one Pi process, not across processes. Use unique paths for concurrent Pi processes: different explicit sessions can overwrite one file even when both `record stop` results are verified. Upstream’s same-session `record start` guard does not reserve the filename across other sessions.
|
|
910
933
|
- `savedFilePath` / `savedFile` for direct `download`, `pdf`, and `wait --download` / `wait -d` saved-file workflows when a host file path is reported or wrapper-verified. Batch results preserve the same fields on the relevant `batchSteps` entry. These fields are metadata only until `artifactVerification` verifies the file. For simple loopback `download <selector> <path>` anchors with a non-ref selector, `details.downloadRecovery.method: "direct-anchor-fetch"` means the wrapper resolved the anchor URL in-session and saved the in-page HTTP(S) response directly to the requested path before using upstream's click/download fallback; non-loopback/profile downloads stay upstream-owned so external provider behavior is preserved.
|
|
911
|
-
- `batchSteps[].artifacts` for per-step artifacts in `batch` output; top-level `artifacts` and `artifactManifest` coalesce an earlier pending recording into the later saved, missing, or stale terminal result for the same namespace/session identity. `record restart` includes both the previous recording it finalized (or an explicit missing/stale failure) and the new pending recording; missing/stale terminal rows retire the prior pending manifest row. A successful later `close` / `quit` / `exit`
|
|
912
|
-
- `artifactVerification` for a normalized verification summary on the unified result and on each successful `batchSteps[]` row
|
|
913
|
-
- `fullOutputPath` / `fullOutputPaths` when parse-valid large snapshot output or other oversized tool output is compacted and spilled to a private file; persisted sessions keep that path under a private session-scoped artifact directory with a
|
|
914
|
-
- `artifactManifest` for a bounded, metadata-only inventory of recent session artifacts. Entries include path metadata, optional canonical `namespace` plus `session` lifecycle identity, artifact `kind`, source `command`/`subcommand` when safe, `storageScope` (`persistent-session`, `process-temp`, or `explicit-path`), and `retentionState` (`live`, `ephemeral`, `missing`, or `evicted`). The default recent window is 100 entries and can be configured with `PI_AGENT_BROWSER_SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES`. A successful session close retires only that exact namespace/session identity's pending recording rows; the separate active reservation index remains authoritative even if this bounded display inventory evicts them. Only the newest pending recording row per namespace/session identity remains live in the manifest. The manifest must not store command args, output contents, headers, DOM snapshots, or downloaded file contents.
|
|
934
|
+
- `batchSteps[].artifacts` for per-step artifacts in `batch` output; top-level `artifacts` and `artifactManifest` coalesce an earlier pending recording into the later saved, missing, or stale terminal result for the same namespace/session identity. `record restart` includes both the previous recording it finalized (or an explicit missing/stale failure) and the new pending recording; missing/stale terminal rows retire the prior pending manifest row. A successful later `close` / `quit` / `exit` retires an earlier unfinalized pending recording as `subcommand: "close-abandoned"`, clears its stop action, and leaves its file unverified. Batch presentation checks the path: only a confirmed absent file becomes `status: "missing"`; a present or inaccessible file stays unverified. It updates aggregate verification/manifest state consistently; a later successful `record stop` replaces that intermediate abandoned row with its saved artifact. Close also resets ref/page/network-route state produced by earlier rows; later lifecycle-proven browser launches, including `record stop`, can rebuild that state without triggering stale pre-close `about:blank` recovery, explicitly non-launching diagnostics cannot, and unknown later rows stay conservatively active. Per-step history remains unchanged. When any later call on the same namespace/session fails while a recording remains pending, `nextActions` combines its normal recovery with exact `stop-pending-recording` args and visible cleanup guidance; the same applies at top level when a later batch step fails. After reload in a non-Git checkout or with managed restore disabled, a live daemon without current-instance provenance cannot accept a stop. A tracked Electron attachment can rebuild that proof through the live debug-endpoint check described above; generic restore-disabled sessions cannot. That policy refusal includes `managedSessionCleanupOnlyReason: "restore-disabled-daemon-without-provenance"` plus the exact `sessionName`/`namespace`, including on implicit calls. It replaces the impossible stop with `close-pending-recording`, an exact close without `sessionMode: "fresh"`. Close retires the recording as `close-abandoned`; any file it leaves is unverified. Same-instance recordings and supported durable-Git reloads still use stop and normal WebM verification.
|
|
935
|
+
- `artifactVerification` for a normalized verification summary on the unified result and on each successful `batchSteps[]` row and on failed recording rows whose receipt identifies an artifact. Top-level `batch` verification rolls up all step file artifacts; each step’s summary reflects that step’s nested tool presentation (including its spill paths and manifest slice). It reports `verified`, `verifiedCount`, `missingCount`, `pendingCount`, `unverifiedCount`, and `artifacts[]` entries with `path`, optional `absolutePath`, optional `requestedPath`, `kind` (a normal file artifact kind or `"spill"` for manifest-backed rows), optional `mediaType`, optional `exists`, optional `sizeBytes`, optional `updatedAtMs`, optional `status`, optional `retentionState` / `storageScope` on manifest-derived rows, `state` (`verified`, `missing`, `pending`, or `unverified`), and optional `limitation` (human-readable lifecycle or retention context, for example pending `record start` / `record restart`, missing, stale, or otherwise unverified files, ephemeral spill files, or evicted persisted spills). The summary `verified` boolean is true only when every entry is `verified`. `record start` / `record restart` are `pending` until `record stop`; `state load` may mention a path in command output but is not a saved artifact row.
|
|
936
|
+
- `fullOutputPath` / `fullOutputPaths` when parse-valid large snapshot output or other oversized tool output is compacted and spilled to a private file; persisted sessions keep that path under a private session-scoped artifact directory for reload/resume, with a per-session byte budget by default; `PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES=0` disables automatic eviction. Malformed oversized upstream output is discarded after parsing, is omitted from `details.stdout`, and reports `fullOutputUnavailable` instead of creating a parse-failure spill.
|
|
937
|
+
- `artifactManifest` for a bounded, metadata-only inventory of recent session artifacts. Entries include path metadata, optional recording receipt/start-window metadata, canonical `namespace` plus `session` lifecycle identity, artifact `kind`, source `command`/`subcommand` when safe, `storageScope` (`persistent-session`, `process-temp`, or `explicit-path`), and `retentionState` (`live`, `ephemeral`, `missing`, or `evicted`). The default recent window is 100 entries and can be configured with `PI_AGENT_BROWSER_SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES`. A successful session close retires only that exact namespace/session identity's pending recording rows; the separate active reservation index remains authoritative even if this bounded display inventory evicts them. Only the newest pending recording row per namespace/session identity remains live in the manifest. The manifest must not store command args, output contents, headers, DOM snapshots, or downloaded file contents.
|
|
915
938
|
- `artifactRetentionSummary` with a concise count of live, evicted, ephemeral, and missing artifacts from the current manifest; results append this summary to model-facing text only when retention state affects recovery, such as spill files, ephemeral files, or evictions. Routine explicit saved files keep the summary in details to avoid noisy browsing transcripts.
|
|
916
939
|
- `artifactCleanup` after a successful close command (`close`, `quit`, or `exit`) only when `artifactManifest` contains at least one existing explicit artifact path. Fields: `owner: "host-file-tools"`, `summary` (same retention summary string as `artifactRetentionSummary` for that manifest), `note` explaining that browser close commands do not delete explicit screenshots/downloads/PDFs/traces/HAR/recordings, and `explicitArtifactPaths`: up to ten **distinct existing** paths taken from manifest rows with `storageScope: "explicit-path"` in encounter order (de-duplicated after checking the filesystem); deleted/stale explicit paths are skipped. When the recent window has only spill/ephemeral inventory or explicit paths already deleted, the field and visible cleanup guidance are omitted. The visible close text stays compact and points operators to `details.artifactCleanup.explicitArtifactPaths` instead of listing paths inline. The native browser tool intentionally does not expose a delete operation for arbitrary user-chosen artifact paths; agents should inspect `artifactVerification` / manifest metadata, then remove files with normal host file tools when cleanup is required.
|
|
917
940
|
- compact **snapshot** metadata on successful presentation when `details.data.compacted` is true (oversized trees): `previewMode` (`"structured"` vs outline `"outline"`), `structuredPreviewUsed`, `previewRefIds`, `previewSections` (per-section `linesShown` / `omittedLines` / root `role` / `title`), `additionalSectionsOmitted`, counts such as `refCount`, `snapshotLineCount`, and `roleCounts`, optional `highValueControlRefIds` aligned with the visible bounded `Omitted high-value controls` lines, and optional `spillError` when the wrapper could not write the redacted spill file; the model text still ends with `Full redacted snapshot path:` or an explicit unavailable reason plus `details.fullOutputPath` when a path exists
|
|
@@ -921,9 +944,38 @@ Additional structured fields can appear when relevant:
|
|
|
921
944
|
- `versionValidation` on a browser-backed preflight failure when installed upstream output is not a stable version at or above the supported floor; it includes `expected` and optional parsed `observed`, while top-level `expectedVersion` remains the recommended current baseline, `minimumSupportedVersion` reports the floor, and `observedVersion` reports the installed version. The extension caches a successful `agent-browser --version` probe per cwd/PATH for the Pi process; plain help/version, close recovery, and sessionless local commands remain available without this browser-backed gate.
|
|
922
945
|
- `agentBrowserStarted` on results that reached browser-run processing: `false` proves the requested main subprocess never started (for example a socket-path, policy, or spawn preflight failure); `true` proves only that the CLI started, not that Chrome launched. Use `details.lifecycle.effectiveLaunch.browserLaunched` for the latter. Preparation helpers may already have touched the isolated session, so script leases always take the normal fail-closed cleanup path.
|
|
923
946
|
|
|
924
|
-
When the tool echoes `args` or `effectiveArgs` back into Pi, sensitive values such as `--headers`, proxy credentials, and auth-bearing URL parameters
|
|
947
|
+
When the tool echoes `args` or `effectiveArgs` back into Pi, sensitive values such as `--headers`, proxy credentials, and auth-bearing URL parameters are redacted first. Replacements use `[REDACTED]` (URL-encoded in parsed URLs). Ordinary technical prose such as `bearer token`, `bearer authentication`, and `bearer credentials` stays verbatim; credential fields, Authorization / Proxy-Authorization headers, and explicit header arguments such as `curl -H` still redact their values. Outside those contexts, bearer redaction requires a value matching bearer-token syntax with digits or token punctuation, not just a following word, HTML, or URL.
|
|
948
|
+
|
|
949
|
+
URL redaction covers `code`, SAMLRequest, SAMLResponse, RelayState, and `authorization_session_id` (including common separator/case variants); `state` and `nonce` are redacted only when the same URL token has an auth/login/OAuth/OIDC/SAML/SSO context or another known sensitive query name, so ordinary application state URLs remain useful. URLs needing no redaction keep their original spelling. Exact internal page-target URLs stay unredacted for browser correctness; model-facing content, structured details, persisted spills, and explicit `outputPath` exports receive the same redacted copies.
|
|
950
|
+
|
|
951
|
+
For parse-valid oversized snapshots and other oversized tool outputs, details should switch to a compact metadata object and include `fullOutputPath` pointing at a private spill file with the full redacted upstream payload. When the caller supplied `outputPath`, only matching live wrapper-manifest spills may provide pre-compaction payloads; direct compacted data, compacted result rows, and command-redacted whole-batch data are rehydrated in place, while any unavailable required spill fails without writing compact metadata. Malformed oversized output is not safe to redact structurally, so its temporary subprocess spill is deleted and no durable `fullOutputPath` is returned. The model-facing tool text should print the actual spill-file path when one exists instead of only saying to inspect a details key. Oversized batch/job/qa failures include bounded failed-step context inline before the preview so agents can see the failed assertion/error and failure category without opening the spill file. Persisted sessions should keep that spill file under a private session-scoped artifact directory so the path remains usable after reload/restart. The oldest persisted spill files are evicted as needed to stay within `PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES` (default 32 MiB), and those evictions are reported as `artifactManifest.entries[].retentionState: "evicted"` instead of silently disappearing from the session inventory. Set `PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES=0` to retain existing and new persistent spill files without automatic byte-budget eviction; unset or invalid values use the default, and positive integer limits retain oldest-first eviction. This does not recover previously evicted files or change temporary subprocess spill cleanup. This persisted-spill byte budget is separate from the recent metadata window controlled by `PI_AGENT_BROWSER_SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES`.
|
|
925
952
|
|
|
926
|
-
|
|
953
|
+
## Recording receipts and recovery
|
|
954
|
+
|
|
955
|
+
Detailed native browser identity, browser-independent native read/confirm handling, and the receipt fields below depend on companion upstream fixes not yet included in the current recommended release. The integration consumes additive fields without version-specific shims or automatic upgrades. Older supported versions keep unavailable measurements `null`/unknown. Native `current`/`last` receipts live in the daemon's memory; Pi can retain receipt metadata in its transcript, but cannot retrieve a missing terminal receipt after that daemon exits.
|
|
956
|
+
|
|
957
|
+
`details.artifacts[].recording` and the corresponding `artifactVerification.artifacts[].recording` contain the native receipt, normalized with explicit unknowns:
|
|
958
|
+
|
|
959
|
+
- `recordingId`, `path`, nullable native `success` and `error`; top-level `frames` means written frames, including held frames, while `capturedFrames` counts received/decoded frames including dropped frames, not pixel-unique frames.
|
|
960
|
+
- `capture.startedAt`, `endedAt`, `firstFrameAt`, `lastFrameAt`: native UTC timestamps. `durationMs`, `firstFrameAfterMs`, `lastFrameAfterMs`, `averageFps` and `maxFrameGapMs` are native elapsed-time measurements; `timestampSource` identifies local receive timing.
|
|
961
|
+
- `output.frames`, `fps`, `encodedFrames`, `durationMs`, `durationSource`, `heldFrames`, `droppedFrames`, `skippedFrames`, and nullable `encoderSucceeded`. Encoded frames come from native encoder progress. Output duration is separate from capture wall duration; legacy `frames / fps` is never used to invent capture duration or captured-frame rate.
|
|
962
|
+
- `file.exists` / `sizeBytes` preserve the native report; the enclosing artifact independently carries wrapper-checked existence, size and freshness. `warning` explains that repaint-driven, repeated, held, static or late/final-only frames cannot establish UI smoothness. Nominal/output FPS is not capture rate.
|
|
963
|
+
|
|
964
|
+
Direct, failed and batched stops retain their data. Restart retains the native `previousRecording` outcome beside the new pending take; a failed previous receipt fails artifact delivery without hiding the newly started recording. A legacy restart file without a terminal native receipt remains unverified, not saved. Recording artifacts can use `status: "failed"` or `"unverified"` even when a file exists. Such evidence stays `artifact-unverified` in successful result categories, including when a later take is pending; filesystem presence cannot turn it into `artifact-saved`. `recordingStartedAtMs` and a known recording ID extend the existing namespace/session reservation journal; they do not create a second store.
|
|
965
|
+
|
|
966
|
+
A wrapper stop timeout or `No recording in progress` response triggers **one** native `session info` query with a two-second subprocess limit. `details.recordingRecovery` records `source: "session-info"`, `status` (`recovered`, `pending`, `failed`, `unverified`, `unavailable`, or `mismatch`), `reason`, actual session/namespace, `expected` reservation metadata, a matching `receipt` when available, `healed`, and the original `attempt` (`success: false`, `exitCode`, `timedOut`, `error`, optional `parseError`). No stop is automatically repeated.
|
|
967
|
+
|
|
968
|
+
Matching requires the actual namespace/session, expected path and known recording ID. When a timed-out batch has no start response, its effective native raw-argument-or-stdin plan and capture start window provide the match instead. A last receipt at a reused path cannot supply the current take's measurements. Recovery needs terminal native success, positive encoder measurements and a matching verified file; filesystem presence alone is insufficient. Freshness starts at the recorded capture window, not the later status query. Missing, mismatched, failed or unfinished evidence stays failed/pending/unverified and retains exact status guidance; a stop follow-up is offered only for a matching current pending take. Unrelated failed batch steps retain their repair actions. A receipt can verify one recording without proving an otherwise unobserved timed-out batch succeeded.
|
|
969
|
+
|
|
970
|
+
`healed: true` can turn the final result into success, but original `exitCode`, `timedOut` and failed-attempt provenance remain visible and in recording `outputPath` envelopes. A failed/empty receipt is still exportable. Error receipts and artifact aliases never become saved-video success wording; JSON mode remains parseable.
|
|
971
|
+
|
|
972
|
+
## Browser-independent read confirmations
|
|
973
|
+
|
|
974
|
+
`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.
|
|
975
|
+
|
|
976
|
+
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 script-owned identities 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.
|
|
977
|
+
|
|
978
|
+
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.
|
|
927
979
|
|
|
928
980
|
## High-value result rendering
|
|
929
981
|
|
|
@@ -933,14 +985,14 @@ The TUI renderer is user-facing only. It may compact or colorize what the human
|
|
|
933
985
|
|
|
934
986
|
Worth doing in v1:
|
|
935
987
|
- screenshots → saved-path summary, visible artifact metadata, `details.artifacts` metadata, and inline image attachment when safe; screenshot paths that upstream would treat ambiguously, such as `.dogfood/run/foo.png`, are normalized to absolute paths before launch and repaired from upstream temp output when possible
|
|
936
|
-
- file artifacts such as PDFs, downloads, `wait --download` / `wait -d` files, `state save` state files, diff screenshot output images, traces, CPU profiles, completed
|
|
988
|
+
- file artifacts such as PDFs, downloads, `wait --download` / `wait -d` files, `state save` state files, diff screenshot output images, traces, CPU profiles, completed video recordings, and path-bearing HAR captures → concise saved-path summaries plus metadata in `details.artifacts` and bounded recent metadata in `details.artifactManifest`; `record start` / `record restart` report recording lifecycle state and the future output path without adding a missing manifest entry, and `record restart` can also report the previous wrapper-known recording that was finalized by the restart; native 0.37 checks `ffmpeg` before starting video capture, while older supported natives may defer failure; successful start/restart calls without ffmpeg expose `details.recordingDependencyWarning` and leave output unverified until checked after stop; direct saved-file workflows also expose `details.savedFilePath` / `details.savedFile`; large or binary artifacts are not inlined into model context; the recent manifest cap can age out explicit-file metadata but does not remove explicit saved files from disk
|
|
937
989
|
- `diff screenshot` → same file-artifact pattern as above for the **diff** image path only (summary text uses “Saved diff image” only when the diff output exists; missing output says “Diff image reported; file not verified” and fails as `artifact-missing`); baseline paths and other fields stay in the structured payload but are not echoed as separate saved artifacts in the visible artifact block, and there is no Pi inline image attachment for the diff output
|
|
938
990
|
- `state load` → completion text may mention the loaded path, but the wrapper does **not** treat that path as a new saved artifact (`artifacts` / `artifactManifest` stay unset) the way `state save` does
|
|
939
991
|
- auth, cookies, storage, clipboard, dialog, frame, state, network, debug, diff, stream, dashboard, chat, and other structured results → concise summaries that avoid expanding secret-bearing payloads; `state show` exposes metadata only in visible text and redacts every cookie/localStorage/sessionStorage `value` in structured details; credential-like keys, values, URLs, body snippets, bearer/basic credentials, clipboard write text, cookie values, and likely secret storage values are redacted before model-facing output and `details.data`, while benign primitive storage values may remain visible for local QA
|
|
940
992
|
- TUI display → custom `agent_browser` call/result rendering with colorized command/output text and a built-in-style collapsed view for long visible output; top-level native modes render as `agent_browser qa → batch --bail`, `agent_browser job → batch --bail` by default (`agent_browser job → batch` when `failFast:false`), or `agent_browser semanticAction → find …` so reviewers can see both the native input mode and compiled upstream command; failed results keep `resultCategory` / `failureCategory` visible before truncated output; `ctrl+o` expansion reveals the full rendered tool result without changing the model-facing content
|
|
941
993
|
- snapshots → origin + ref count + main-content-first compact preview, with the redacted snapshot spill path printed directly in content and kept in `details.fullOutputPath` plus `details.artifactManifest` when the inline result would otherwise be too large
|
|
942
994
|
- oversized generic outputs such as large `eval --stdin` payloads → compact preview plus the actual spill file path instead of dumping the whole payload into model context
|
|
943
|
-
- `read [url]` → upstream `data.content` first, with source/content-type/status/final-URL metadata retained in `details.data
|
|
995
|
+
- `read [url]` → upstream `data.content` first, with source/content-type/status/final-URL metadata retained in `details.data`. Explicit URL reads and all-read batches neither allocate/replace a managed browser nor run pre-, post- or timeout page helpers; malformed read syntax is left to native validation rather than treated as a DOM read. Calls targeting an already-owned session retain its daemon settings and launch metadata, including after reload/resume, without changing restore policy or consuming a pending page reopen. Fetched URLs do not replace the browser target or invalidate refs. `Read execution` and `details.readSource` / `agentBrowserStarted` / native `lifecycle` report command evidence, not shared-browser liveness; absent launch evidence stays unknown. Bare `read` keeps normal DOM verification. Explicit read timeouts retain the native `.md` / ancestor-`llms.txt` request budget.
|
|
944
996
|
- extraction-style commands like `eval --stdin` and `get title` → scalar-first text with lightweight origin context when available
|
|
945
997
|
- navigation actions like `click`, `back`, `forward`, and `reload` → lightweight post-action title/url summary when available
|
|
946
998
|
- tab lists → compact summary/table
|
|
@@ -957,13 +1009,15 @@ If `agent-browser` is not on `PATH`, fail with a message that:
|
|
|
957
1009
|
|
|
958
1010
|
## Session behavior
|
|
959
1011
|
|
|
1012
|
+
`session info` is one explicit, read-only preflight, not an automatic probe before every command. Its text distinguishes daemon `active` / `pid` from native `runtime.browser`: `status`, nullable `alive`, Chrome `pid`, exact `userDataDir`, `tabs`, native `ownership` (`launched`, `attached`, `none`, or `unknown`), and `error`. It also preserves native `runtime.recording.current` / `last` receipts and protocol `capabilities`. `data.piCleanupOwnership` separately reports `caller-owned` or `wrapper-managed` from Pi's actual ownership records; an explicit name can still target a wrapper-managed session. The integration does not infer a Chrome PID/profile from config, scan host processes, launch Chrome or change tabs for this preflight. Missing native fields, an active daemon with `runtime: null`, and legacy `browserLaunched` alone do not prove live browser identity. Name-only `session` responses select a session without proving liveness. Restore check URLs, text and code remain omitted from status text.
|
|
1013
|
+
|
|
960
1014
|
- maintain one extension-managed active session per `pi` session for the common path
|
|
961
1015
|
- derive the base implicit session name from the official `pi` session id plus a cwd hash so same-named checkouts do not collide
|
|
962
|
-
- respect explicit upstream `--session` with minimal interference
|
|
1016
|
+
- respect explicit upstream `--session` and configured native session/namespace defaults with minimal interference; per-call `--config` reaches helpers without changing native precedence
|
|
963
1017
|
- treat the extension-managed session as convenience state owned by the wrapper
|
|
964
1018
|
- preserve the current branch-visible extension-managed session across `/reload`, exact-session relaunch, `/resume`, and Pi `session_tree` branch transitions so persisted sessions can keep following the live browser after lifecycle changes
|
|
965
1019
|
- close the active extension-managed session when the originating `pi` process quits, while leaving explicit caller-provided sessions alone
|
|
966
|
-
- set one idle timeout on extension-managed sessions as a backstop for abnormal exits or cleanup failures, and pass that same `AGENT_BROWSER_IDLE_TIMEOUT_MS` to top-level commands plus every
|
|
1020
|
+
- set one idle timeout on extension-managed sessions as a backstop for abnormal exits or cleanup failures, and pass that same `AGENT_BROWSER_IDLE_TIMEOUT_MS` to top-level commands plus every helper targeting that owned session; caller-owned sessions retain native idle policy so upstream does not restart the background browser, reset the active tab, or discard current refs between a snapshot and action
|
|
967
1021
|
- clean up process-private temp spill artifacts on shutdown, while keeping persisted-session snapshot spill files in a private session-scoped artifact directory so `details.fullOutputPath` survives reload/restart and the oldest spill files are evicted if the per-session artifact budget is exceeded
|
|
968
1022
|
- reconstruct the current branch-visible extension-managed session, every transcript-proven still-active wrapper-owned identity, latest page-scoped refs, newest-revision aggregate `artifactManifest`, and wrapper-tracked Electron launch records from the active transcript branch on `session_start` and Pi `session_tree` so later default and explicit off-current calls keep following owned managed browsers and can continue reporting artifact retention state; successful explicit wrapper-owned close rows and `electron.cleanup` managed-session steps are restore-visible close events
|
|
969
1023
|
- keep runtime cleanup ownership separate from branch-visible state: `session_tree` restore and wrapper-owned browser commands are serialized with managed-session work; caller-owned explicit-session commands use separate process-local queues keyed by effective canonical namespace/session (explicit namespace argv wins over inherited `AGENT_BROWSER_NAMESPACE`, including an explicit empty default), so the live URL probe, preparation helpers, semantic snapshot, main command, and state commit for one identity cannot interleave while different identities remain concurrent. Namespace-scoped `close --all` is the exception: it drains and exclusively barriers managed plus matching caller-owned work before clearing global namespace state. Namespace and session identity components are additionally Unicode-normalized and case-folded on macOS and Windows to match their case-insensitive daemon paths. Only the outer tool execution acquires that key; nested helpers run under it without re-entry. Policy, route, and artifact deltas survive unrelated managed-state commits, while a separate branch-restore generation guard prevents stale completions from overwriting a newer branch. Concurrent artifact-producing results carry a monotonic aggregate manifest revision so transcript replay selects the complete bounded manifest rather than whichever call happened to occupy the last row. Extension-managed sessions and wrapper-launched Electron records owned by the current process remain eligible for quit/cleanup, and fresh-session allocation stays monotonic across branch restores, including auto rows and close rows that reference wrapper-generated fresh names
|
|
@@ -973,18 +1027,18 @@ If `agent-browser` is not on `PATH`, fail with a message that:
|
|
|
973
1027
|
- when an unnamed `sessionMode: "fresh"` launch fails or times out, preserve the previous managed session when one was active or report the attempted fresh session as abandoned when no managed session was active (`details.managedSessionOutcome`; visible `Managed session outcome: …` when the final tool call used `sessionMode: "fresh"` and failed, or when automatic close of its replaced session failed—see `#details`)
|
|
974
1028
|
- if that unnamed fresh launch replaced an already-active managed session, best-effort close the old managed session after the switch succeeds; `details.managedSessionOutcome.replacedSessionClosed` records the cleanup result, and `false` keeps the older identity wrapper-owned across transcript resume for explicit follow-up or cleanup
|
|
975
1029
|
- treat every explicit caller-provided `--session` as user-managed, including `piab-*` names. Wrapper-owned implicit sessions set a Pi-transcript- and Git-checkout-generation-scoped `AGENT_BROWSER_RESTORE` key automatically unless disabled with `PI_AGENT_BROWSER_MANAGED_SESSION_RESTORE=0`; explicit caller sessions do not receive that injection unless they exactly target the current wrapper-owned identity. Caller state/restore paths, profiles, upstream config, file access, launch arguments, environment variables, local file pages, `outputPath`, and close arguments pass through unchanged. `session list` and `state list` keep all upstream rows and restore identifiers visible. Automatic restore still validates and pins its own checkout/storage/namespace identity and coordinates same-daemon reuse so the wrapper cannot mix restore pools or corrupt managed lifecycle state. Ambiguous tab, attachment, history, script, or state-load transitions remain page-target correctness boundaries: content calls live-check `get url` or require explicit navigation before acting. Windows uses `cross-spawn` for native executable and `.cmd` argument transport, rather than PowerShell or wrapper-owned argument reordering. Empty operands such as `fill #field ""`, `--args ""`, and explicit default `--namespace ""`, literal doublequotes in fill text, and command/subcommand adjacency are retained. Upstream receives the empty namespace rather than a wrapper omission or environment workaround. The selected child `PATH` shim is not bypassed; POSIX keeps native Node `spawn`.
|
|
976
|
-
- before a content-bearing read or interaction against a caller-owned explicit session or established attachment, run a session-scoped `get url` probe so stale transcript state cannot target the wrong page. A failed or non-URL probe blocks the requested content command. The process-local namespace/session queue keeps that probe atomic with semantic snapshot resolution and the main command inside one extension instance. Nested `batch` steps remain unsupported; raw batch command strings mirror upstream's ASCII-space tokenizer, including quoting and backslash handling.
|
|
1030
|
+
- before a DOM/content-bearing read or interaction against a caller-owned explicit session or established attachment, run a session-scoped `get url` probe so stale transcript state cannot target the wrong page. A failed or non-URL probe blocks the requested content command. The process-local namespace/session queue keeps that probe atomic with semantic snapshot resolution and the main command inside one extension instance. Nested `batch` steps remain unsupported; raw batch command strings mirror upstream's ASCII-space tokenizer, including quoting and backslash handling.
|
|
977
1031
|
- pass explicit `--profile` straight through to upstream `agent-browser`; no profile-cloning or isolation layer is added in v1
|
|
978
1032
|
<!-- agent-browser-playbook:start wrapper-tab-recovery -->
|
|
979
1033
|
<!-- Generated from extensions/agent-browser/lib/playbook.ts. Run `npm run docs -- playbook write` to update. -->
|
|
980
1034
|
- After open/goto/navigate calls with --profile, --restore, --session-name, or --state, agent_browser best-effort re-selects the tab whose URL matches the returned page when restored tabs steal focus during launch or reconnect.
|
|
981
|
-
- After confirmed shutdown of an automatically restored managed session, the wrapper retains its complete recorded URL, including the fragment, until the first current-page operation (including get url and reload). Non-page calls such as tab list
|
|
1035
|
+
- After confirmed shutdown of an automatically restored managed session, the wrapper retains its complete recorded URL, including the fragment, until the first current-page operation (including get url and reload). Non-page calls such as tab list may start a daemon without fulfilling that reopen; explicit URL reads leave the managed browser and pending reopen untouched. The wrapper uses native open once, verifies the observed tab, and discards old refs/frame scope; it does not restore unsaved forms, JavaScript memory, or history. Explicit navigation, caller-owned/attached sessions, and restore-disabled sessions are not auto-reopened.
|
|
982
1036
|
- For a still-live browser after tab drift or resume, the wrapper verifies/selects the intended tab before ref/semantic helpers and page commands; failed selection stops the call without navigating. Local commands, read <url>, URL a11y/vitals, diff url, window new, and explicit tab/navigation/connection/state recovery do not require the prior tab. Batch checks follow effective rows past non-page prefixes and stop at explicit context changes, preserving caller argv/stdin and continue-on-error behavior. Same-tab reselection is avoided because it clears refs. Use exact batch --bail for fail-fast, not --bail=<value>. Routine same-session calls skip tab-list preflights.
|
|
983
1037
|
- For sessions with observed tab-drift risk, after a successful command on a known target tab, agent_browser also best-effort restores that intended tab if a restored/background tab steals focus after the command completes. Routine same-session commands skip this post-command tab-list probe.
|
|
984
1038
|
- If a known session target unexpectedly reports about:blank, agent_browser best-effort re-selects the prior intended target when it still exists; if recovery fails, it records the observed about:blank target and reports exact recovery guidance instead of treating the prior page as active.
|
|
985
1039
|
- If upstream reports tab_gone, the pinned bound tab is gone; use details.nextActions (tab list / tab new) instead of assuming another tab is yours.
|
|
986
1040
|
<!-- agent-browser-playbook:end wrapper-tab-recovery -->
|
|
987
|
-
- on local Unix launches, set a short private socket directory for wrapper-spawned `agent-browser` processes so extension-generated session names do not fail the upstream Unix socket-path length limit in longer cwd/session-name combinations; require an absolute non-symlink directory owned by the current uid with mode `0700`, otherwise fail before spawn. Socket checks trust the operating environment's actual `/`, not its reported UID; all non-root ownership, permission and alias-destination checks remain in force. This is not protection from the controller of the root filesystem; see [socket trust](ARCHITECTURE.md#ownership). Android/Termux uses a short directory under the owner-only `/data/data/<package>` app sandbox, compacts generated managed identities to one 80-bit digest so ordinary namespace plus fresh-session paths remain within the limit, stores policy-lock coordination under `os.tmpdir()`, and probes process identity with Termux's `ps` beside Node instead of unavailable `/bin/ps`
|
|
1041
|
+
- caller-owned sessions honor native `AGENT_BROWSER_SOCKET_DIR` unless the wrapper-specific socket override is set, using the same integrity checks; on other local Unix launches, set a short private socket directory for wrapper-spawned `agent-browser` processes so extension-generated session names do not fail the upstream Unix socket-path length limit in longer cwd/session-name combinations; require an absolute non-symlink directory owned by the current uid with mode `0700`, otherwise fail before spawn. Socket checks trust the operating environment's actual `/`, not its reported UID; all non-root ownership, permission and alias-destination checks remain in force. This is not protection from the controller of the root filesystem; see [socket trust](ARCHITECTURE.md#ownership). Android/Termux uses a short directory under the owner-only `/data/data/<package>` app sandbox, compacts generated managed identities to one 80-bit digest so ordinary namespace plus fresh-session paths remain within the limit, stores policy-lock coordination under `os.tmpdir()`, and probes process identity with Termux's `ps` beside Node instead of unavailable `/bin/ps`
|
|
988
1042
|
- keep wrapper-spawned commands bounded by clamping `AGENT_BROWSER_DEFAULT_TIMEOUT` to the upstream documented default of 25 seconds while the default wrapper child-process watchdog is 35 seconds (`PI_AGENT_BROWSER_PROCESS_TIMEOUT_MS` overrides it, and top-level `timeoutMs` overrides it per call for browser CLI subprocesses). Explicit `wait <ms>`, `wait --timeout <ms>`, and WebMCP `invoke` / `result --timeout <ms>` calls can exceed that default; when top-level `timeoutMs` is omitted, the wrapper derives a per-call subprocess watchdog from the requested command duration plus a small grace window. Dialog commands use `PI_AGENT_BROWSER_DIALOG_PROCESS_TIMEOUT_MS` (default 5000 ms), and click/tap/find refs or tokens plus `eval --stdin` snippets whose text looks like alert/confirm/prompt/dialog triggers use `PI_AGENT_BROWSER_DIALOG_TRIGGER_PROCESS_TIMEOUT_MS` (default 8000 ms). Timed-out compiled `job` / `qa` or caller `batch` calls may add `details.timeoutPartialProgress` and visible `Timeout partial progress` evidence with per-step status, retry payloads, current page title/URL, and declared artifact path checks; timed-out dialog-like commands may add dialog status/dismiss/fresh-session recovery next actions
|
|
989
1043
|
- interactive or long-running upstream families such as `chat` without a prompt, `dashboard start`, `stream enable`, `trace start`, `profiler start`, `record start`, `inspect`, `install`, `upgrade`, `doctor --fix`, and `confirm-interactive` are passed through thinly but remain bounded by the same wrapper timeout/session planning rules; prefer explicit arguments, single-shot `chat <message>`, non-interactive flags like `doctor --offline --quick` or `doctor --json`, and cleanup pairs such as `dashboard stop`, `stream disable`, `trace stop`, `profiler stop`, and `record stop`
|
|
990
1044
|
- treat successful plain-text inspection commands like `--help` and `--version` as stateless: do not inject the implicit managed session and do not let those calls claim the managed-session slot
|
package/package.json
CHANGED