pi-agent-browser-native 0.6.7 → 0.6.8
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 +16 -0
- package/README.md +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +2 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +3 -1
- package/dist/extensions/agent-browser/lib/playbook.js +1 -1
- package/dist/extensions/agent-browser/lib/process.js +5 -106
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +5 -2
- package/dist/extensions/agent-browser/lib/results/envelope.js +5 -3
- package/dist/extensions/agent-browser/lib/results/next-actions.js +8 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +10 -2
- package/dist/extensions/agent-browser/lib/results/presentation.js +9 -2
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/COMMAND_REFERENCE.md +3 -1
- package/docs/SUPPORT_MATRIX.md +5 -2
- package/docs/TOOL_CONTRACT.md +5 -3
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.8 - 2026-09-07
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Add session- and namespace-aware `inspect-overlay-state` recovery for direct, semantic, raw `find` (including `nth` and default-click), and batched/job clicks that upstream rejects because another element covers the target's click point. These failures remain `upstream-error`; the wrapper recommends refreshing refs for inspection without retrying the blocked click or guessing a dismiss control. Thanks to @MDGChamomile for #147.
|
|
8
|
+
|
|
9
|
+
- Replace Windows PowerShell argument forwarding with `cross-spawn` at the shared process boundary, preserving empty operands, literal doublequotes, the selected `PATH` shim and upstream architecture selection. Remove command reordering and the old empty-argument/namespace workarounds; POSIX keeps native Node `spawn`. Thanks to @MaartenDMT (#109) and @dagve11 (#134) for their reports and proposed fixes.
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
- Clarify that recording destinations are reserved within one Pi process; concurrent processes must use unique paths because different sessions can overwrite the same file (#110).
|
|
14
|
+
|
|
15
|
+
### Validation
|
|
16
|
+
|
|
17
|
+
- Retain runnable Windows argv and failure/lifecycle contracts in the local and platform-target gates. Native macOS and Ubuntu validate the POSIX process path; PowerShell Legacy diagnostics reproduced both old corruptions but do not validate the replacement Windows transport. Native Windows/`cmd.exe` was unavailable and was not run under the task-specific waiver; permanent release requirements remain intact.
|
|
18
|
+
|
|
3
19
|
## 0.6.7 - 2026-09-07
|
|
4
20
|
|
|
5
21
|
### Fixed
|
package/README.md
CHANGED
|
@@ -449,7 +449,7 @@ Typical pitfalls:
|
|
|
449
449
|
- If the failure is `selector-not-found`, the wrapper may take one fresh snapshot and add `Current snapshot ref fallback` when that snapshot has exact visible role/name matches for the failed `find` / `semanticAction` target. Non-fill targets can include direct `try-current-visible-ref*` next actions, and semantic click misses can still add bounded `Agent-browser candidate fallbacks` such as `button`/`link` role retries for `text` clicks. `semanticAction` does not expose `uncheck` while upstream `find ... uncheck` is not runtime-supported; use raw `args: ["uncheck", <selector-or-ref>]` after a stable selector or fresh snapshot ref. For semantic `fill` misses on desktop or host-controlled rich inputs, prefer `details.richInputRecovery`: refresh refs, choose the current editable `@ref`, focus or click it, then use `keyboard type` for framework-controlled editors that require real key events. `keyboard inserttext` is paste-like and can change a DOM value without updating application state, so use it only with separate application-state verification. Direct contenteditable fills are verified with `get text` when snapshot metadata proves the target is contenteditable; if replacement did not happen, `details.fillVerification` warns before any submit step. Those recovery nextActions do not copy the fill text and do not press `Enter` or submit; only submit when the user flow explicitly calls for it (same contract link).
|
|
450
450
|
- A successful upstream `click` is not proof that the web app handled the event or changed state. For top-level non-Electron direct clicks on `xpath=` targets and eligible current `@e…` refs, the wrapper may fail the tool with `details.clickDispatch` and a `Click dispatch diagnostic` line when upstream reported success but no trusted DOM event reached the resolved target. Raw `find … click` locator calls are not probed because the wrapper has no concrete element before upstream resolves the locator, and document-level probes can falsely fail frame-scoped clicks. `@e…` ref click probes are limited to current snapshot refs with accessible role `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`, with a unique role/name in both the saved snapshot and the live candidates. Duplicate-name refs remain upstream-owned; snapshot order is not target identity. Use the suggested `inspect-click-dispatch-miss` / `retry-click-after-dispatch-miss` next actions instead of assuming the click mutated the page; when `details.clickDispatch.scrollContainer` is present, use `scroll-target-into-view-after-dispatch-miss` first. When the task depends on a mutation, follow `inspect-after-mutation` / `pageChangeSummary` evidence with a wait, URL/text check, or fresh snapshot before trusting the result; if the target still did not change, retry with a current visible ref or stable selector and report the workflow issue instead of silently continuing. For static local fixtures where the user only needs to exercise app code, an explicit `eval --stdin` programmatic click such as `document.querySelector("#demo").click()` can be a diagnostic workaround, but treat it as an untrusted scripted activation rather than proof a real user click works, and never use it to bypass user instructions. Respect explicit user stop boundaries yourself: if the user says to stop before order/post/purchase/submit, gather evidence on that page and do not click the final action. The wrapper does not parse broad prompt text into business-intent action blocks; `details.promptGuard` is reserved for concrete artifact-before-close checks.
|
|
451
451
|
- A successful upstream `click` can deliver no input at all on some sites. Reproduced against `https://www.saucedemo.com/` with upstream `agent-browser` 0.34.0 and no wrapper involved: when every CLI invocation in the flow is spaced ~0.6s apart, the React add-to-cart click reports success while **zero** DOM events reach a capture-phase listener and the cart badge never updates (0/5 runs succeeded, versus 4/5 with no spacing). A single idle gap is harmless (5/5); only sustained spacing across the flow triggers it. In that state a scripted `document.querySelector(...).click()` still works and updates the badge, so the page and its handlers are fine and the input never arrives; retries, `scrollintoview`, headed mode, and re-navigation do not recover it. This looks site-specific — React TodoMVC and react.dev were unaffected under the same pacing and command count. Wrapper calls do more per-command work than raw CLI calls, so they sit in the slow regime more often. Running the interaction steps as one `batch` keeps them inside a single upstream process and succeeded 5/5; prefer `batch` for consecutive real-app click flows, and treat a click whose `pageChangeSummary`/`navigationSummary` shows no change as unproven. When a `wait --url` or `assertUrl` times out after a suspected missed click, use the `fresh-session-after-url-wait-timeout` next action (`sessionMode: "fresh"` + `open about:blank`): replace about:blank with the target URL and replay the flow as one batch in a fresh session instead of retrying the wait.
|
|
452
|
-
- A successful `snapshot -i` can surface `Possible overlay blockers` immediately when refs already contain strong dialog/alertdialog evidence plus close/dismiss controls. If a **top-level** `@e…`/`ref=` click succeeds (unified command `click`, not a `batch` step), upstream reports `data.clicked`, and `details.navigationSummary.url` stays on the same tab URL under the same normalization as ref preflight (fragment-insensitive), the wrapper may take one extra `snapshot -i` and add `Possible overlay blockers` with `details.overlayBlockers` (`candidates`, `summary`, optional `snapshot` refresh for refs) plus session-aware `inspect-overlay-state` / bounded `try-overlay-blocker-candidate-*` next actions when that snapshot shows strong modal context (`dialog` / `alertdialog`) and close/dismiss-like controls. Page-wide words like privacy, sign in, or banner alone do not trigger this diagnostic. The unchanged-URL check compares the prior pinned tab target with `details.navigationSummary.url`; CSS selector clicks do not run this overlay probe. Also skipped when tab correction or about-blank recovery already ran on that result.
|
|
452
|
+
- A successful `snapshot -i` can surface `Possible overlay blockers` immediately when refs already contain strong dialog/alertdialog evidence plus close/dismiss controls. If a **top-level** `@e…`/`ref=` click succeeds (unified command `click`, not a `batch` step), upstream reports `data.clicked`, and `details.navigationSummary.url` stays on the same tab URL under the same normalization as ref preflight (fragment-insensitive), the wrapper may take one extra `snapshot -i` and add `Possible overlay blockers` with `details.overlayBlockers` (`candidates`, `summary`, optional `snapshot` refresh for refs) plus session-aware `inspect-overlay-state` / bounded `try-overlay-blocker-candidate-*` next actions when that snapshot shows strong modal context (`dialog` / `alertdialog`) and close/dismiss-like controls. Page-wide words like privacy, sign in, or banner alone do not trigger this diagnostic. The unchanged-URL check compares the prior pinned tab target with `details.navigationSummary.url`; CSS selector clicks do not run this overlay probe. Also skipped when tab correction or about-blank recovery already ran on that result. Separately, when upstream rejects a click because another element covers the target's click point (`is covered by` … `at its click point`), the wrapper keeps `failureCategory: "upstream-error"` and adds only the session-aware `inspect-overlay-state` snapshot action; it does not retry the blocked click or guess a dismiss control without fresh snapshot evidence. This covers direct `click`, `semanticAction` click, raw `find` clicks (including `nth` and an omitted action that defaults to click), and failed `batch`/`job` rows; it does not diagnose silent input-dispatch failures.
|
|
453
453
|
- If `get text <selector>` reads a non-ref, non-simple-id CSS selector with multiple matches or a hidden first match while visible matches exist, including successful `batch` steps, the wrapper may add `Selector text visibility warning`, `details.selectorTextVisibility` (plus `selectorTextVisibilityAll` for multiple batched warnings), and `inspect-visible-text-candidates` next actions; the warning names the matching `details.nextActions` id. Prefer a visible `@ref`, a scoped selector, or a targeted `eval --stdin` over hidden tab content.
|
|
454
454
|
- In wrapper-tracked attached Electron sessions, broad selectors such as `body`, `html`, `main`, or `[role=application]` may read the whole app shell. The wrapper may add `Broad Electron get text selector warning`, `details.electronGetTextScopeWarning`, and `snapshot-for-electron-text-scope`; ordinary browser pages do not qualify without Electron launch provenance. Prefer `snapshot -i`, a current `@ref`, or a narrower panel selector.
|
|
455
455
|
|
|
@@ -4,7 +4,7 @@ import { isCloseCommand, isOpenNavigationCommand } from "../../command-taxonomy.
|
|
|
4
4
|
import { boundElectronProbeString } from "../../electron/cdp.js";
|
|
5
5
|
import { executableExistsOnPath } from "../../executable-path.js";
|
|
6
6
|
import { formatSessionArtifactRetentionSummary } from "../../results/artifact-manifest.js";
|
|
7
|
-
import { buildNextToolAction, withOptionalSessionArgs } from "../../results/next-actions.js";
|
|
7
|
+
import { buildInspectOverlayStateAction, buildNextToolAction, withOptionalSessionArgs } from "../../results/next-actions.js";
|
|
8
8
|
import { buildVisibleRefFallbackDiagnosticFromSnapshot, getVisibleRefFallbackTarget } from "../../results/selector-recovery.js";
|
|
9
9
|
import { extractRefSnapshotFromData, isAboutBlankUrl, normalizeComparableUrl } from "../../session-page-state.js";
|
|
10
10
|
import { extractUpstreamCommandTokens, parseWaitCommandTokens, redactInvocationArgs, redactSensitiveText } from "../../runtime.js";
|
|
@@ -266,7 +266,7 @@ export function formatOverlayBlockerText(diagnostic) {
|
|
|
266
266
|
return ["Possible overlay blockers:", ...diagnostic.candidates.map((candidate) => `- ${candidate.ref}${candidate.role ? ` ${candidate.role}` : ""}${candidate.name ? ` ${JSON.stringify(candidate.name)}` : ""}: ${candidate.reason}`)].join("\n");
|
|
267
267
|
}
|
|
268
268
|
export function buildOverlayBlockerNextActions(options) {
|
|
269
|
-
return [
|
|
269
|
+
return [buildInspectOverlayStateAction(options.sessionName), ...options.diagnostic.candidates.map((candidate, index) => ({ id: `try-overlay-blocker-candidate-${index + 1}`, params: { args: withOptionalSessionArgs(options.sessionName, candidate.args) }, reason: candidate.reason, safety: "Only click this if the candidate is clearly a close/dismiss control for an overlay that blocks the intended workflow.", tool: "agent_browser" }))];
|
|
270
270
|
}
|
|
271
271
|
export function collectSnapshotOverlayBlockerDiagnostic(data) {
|
|
272
272
|
const candidates = getOverlayBlockerCandidates(data);
|
|
@@ -12,7 +12,7 @@ import { analyzeQaPresetResults, analyzeQaPresetTimeout, buildQaCompactFailureTe
|
|
|
12
12
|
import { applyNetworkRouteRecords, buildNetworkRouteDiagnostics } from "../../results/network-routes.js";
|
|
13
13
|
import { buildToolPresentation } from "../../results/presentation.js";
|
|
14
14
|
import { compactLargePresentationOutput } from "../../results/presentation/large-output.js";
|
|
15
|
-
import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
15
|
+
import { extractEnvelopeErrorText, getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
16
16
|
import { omitUpstreamLifecycle } from "../../results/presentation/common.js";
|
|
17
17
|
import { getClipboardWritePayloadCandidates, redactClipboardPermissionEcho, redactClipboardPermissionErrorValue } from "../../results/presentation/errors.js";
|
|
18
18
|
import { shouldCaptureSemanticActionNavigationSummary } from "../../results/presentation/semantic-action.js";
|
|
@@ -637,6 +637,8 @@ export async function processBrowserOutput(input) {
|
|
|
637
637
|
}
|
|
638
638
|
}
|
|
639
639
|
let errorText = getAgentBrowserErrorText({ aborted: processResult.aborted, command: prepared.executionPlan.commandInfo.command, effectiveArgs: prepared.redactedProcessArgs, envelope: presentationEnvelope, exitCode: processResult.exitCode, parseError, plainTextInspection, staleRefArgs: getStaleRefArgs(prepared.commandTokens, prepared.runtimeToolStdin), spawnError: processResult.spawnError, stderr: processResult.stderr, timedOut: processResult.timedOut, timeoutMs: processResult.timeoutMs, wrapperRecoveryHint: buildWrapperRecoveryHint({ sessionTabCorrection }) });
|
|
640
|
+
if (errorText && presentationEnvelope?.success === false && extractEnvelopeErrorText(presentationEnvelope.error) === undefined)
|
|
641
|
+
presentationEnvelope = { ...presentationEnvelope, error: errorText };
|
|
640
642
|
if (errorText) {
|
|
641
643
|
const clipboardWritePayloadCandidates = getClipboardWritePayloadCandidates(prepared.commandTokens);
|
|
642
644
|
errorText = redactClipboardPermissionEcho(prepared.executionPlan.commandInfo, errorText);
|
|
@@ -53,7 +53,7 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
53
53
|
"On dashboards with nested scroll containers, verify scroll with a screenshot or fresh snapshot -i; if the viewport did not move, details.data.scrolled may be false/noMovement true and you should prefer scrollintoview <@ref> or target the actual scrollable region with scroll <selector> <dir> [px|percent]. For native selects, use select <selector> <value...> (or semanticAction/job select) instead of clicking option refs; for custom comboboxes, a click/semanticAction may only focus the field, so re-snapshot and use keyboard type <text> for focused input, press ArrowDown or press Enter, or visible option refs. Raw type requires both <selector> and <text>.",
|
|
54
54
|
"When using eval --stdin, scope checks and actions to the target element or route whenever possible instead of relying on broad page-wide text heuristics.",
|
|
55
55
|
"When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file, but never reuse a screenshot, download, recording, or other browser artifact destination as outputPath. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. Local file pages and caller-selected output paths are supported when upstream allows them. If get text on a broad CSS selector surfaces details.selectorTextVisibility or selectorTextVisibilityAll, prefer a visible @ref, a more specific selector, or the inspect-visible-text-candidates nextAction over hidden tab content.",
|
|
56
|
-
"When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.",
|
|
56
|
+
"When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If an upstream click failure says the target is covered by another element at the target's click point, use the inspect-overlay-state nextAction to refresh refs and inspect the blocker before deciding whether to retry; do not blindly repeat the blocked click. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.",
|
|
57
57
|
"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.",
|
|
58
58
|
"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.",
|
|
59
59
|
"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.",
|
|
@@ -3,9 +3,9 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { lstat, mkdir, readdir, readlink, stat } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join } from "node:path";
|
|
5
5
|
import { env as processEnv, platform as processPlatform } from "node:process";
|
|
6
|
+
import { spawn as crossSpawn } from "cross-spawn";
|
|
6
7
|
import { parseArgvDescriptor } from "./argv-descriptor.js";
|
|
7
|
-
import {
|
|
8
|
-
import { extractExplicitSessionName, getFlagName, GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, GLOBAL_VALUE_FLAGS, optionalGlobalValueFlagConsumesNext, resolveAgentBrowserNamespace, } from "./argv-grammar.js";
|
|
8
|
+
import { extractExplicitSessionName, resolveAgentBrowserNamespace } from "./argv-grammar.js";
|
|
9
9
|
import { commitManagedSessionRestoreSuppression, getManagedSessionRestoreEnv, getManagedSessionRestoreProtectedEnv, getOwnedManagedSessionCompatibilityEnv, getOwnedManagedSessionNamespaceEnv, isOwnedManagedSessionTarget, validateManagedSessionRestoreContextForSpawn, } from "./managed-session-restore.js";
|
|
10
10
|
import { getPageTargetValidationError, } from "./page-target-validation.js";
|
|
11
11
|
import { getImplicitSessionIdleTimeoutMs } from "./runtime.js";
|
|
@@ -26,114 +26,19 @@ export const SAFE_AGENT_BROWSER_OPERATION_TIMEOUT_MS = 25_000;
|
|
|
26
26
|
const DEFAULT_AGENT_BROWSER_PROCESS_TIMEOUT_MS = 35_000;
|
|
27
27
|
/** Grace period after `exit` before resolving when `close` is delayed by inherited stdio handles. */
|
|
28
28
|
const EXIT_STDIO_GRACE_MS = 100;
|
|
29
|
-
const WINDOWS_AGENT_BROWSER_MISSING_MARKER = "PI_AGENT_BROWSER_COMMAND_NOT_FOUND:agent-browser.cmd";
|
|
30
29
|
const attachedBrowserSessionContext = new AsyncLocalStorage();
|
|
31
|
-
const WINDOWS_COMMANDS_WITH_ADJACENT_SUBCOMMAND = new Set([
|
|
32
|
-
"auth", "clipboard", "cookies", "dashboard", "device", "dialog", "diff", "find", "get", "is", "keyboard",
|
|
33
|
-
"mouse", "network", "plugin", "profiler", "react", "record", "session", "set", "skills", "state", "storage",
|
|
34
|
-
"stream", "tab", "trace", "webmcp", "window",
|
|
35
|
-
]);
|
|
36
30
|
export function withAttachedBrowserSessionContext(preserve, run) {
|
|
37
31
|
return attachedBrowserSessionContext.run(preserve || attachedBrowserSessionContext.getStore() === true, run);
|
|
38
32
|
}
|
|
39
|
-
export function getWindowsExplicitDefaultNamespaceEnv(args, parentNamespace, platform = processPlatform) {
|
|
40
|
-
return platform === "win32" && resolveAgentBrowserNamespace(args, parentNamespace) === ""
|
|
41
|
-
? { AGENT_BROWSER_NAMESPACE: "" }
|
|
42
|
-
: {};
|
|
43
|
-
}
|
|
44
33
|
function appendTail(text, addition, maxChars) {
|
|
45
34
|
const combined = text + addition;
|
|
46
35
|
return combined.length <= maxChars ? combined : combined.slice(combined.length - maxChars);
|
|
47
36
|
}
|
|
48
|
-
function quoteWindowsPowerShellArg(value) {
|
|
49
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
50
|
-
}
|
|
51
|
-
/** Exported for unit tests that lock Windows launcher argv ordering. */
|
|
52
|
-
export function reorderWindowsLeadingGlobalArgs(args) {
|
|
53
|
-
const leadingGlobals = [];
|
|
54
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
55
|
-
const token = args[index];
|
|
56
|
-
if (isKnownCommandToken(token)) {
|
|
57
|
-
if (index === 0)
|
|
58
|
-
return args;
|
|
59
|
-
const firstPositional = args[index + 1];
|
|
60
|
-
return WINDOWS_COMMANDS_WITH_ADJACENT_SUBCOMMAND.has(token) && firstPositional && !firstPositional.startsWith("-")
|
|
61
|
-
? [token, firstPositional, ...leadingGlobals, ...args.slice(index + 2)]
|
|
62
|
-
: [token, ...leadingGlobals, ...args.slice(index + 1)];
|
|
63
|
-
}
|
|
64
|
-
if (!token.startsWith("-"))
|
|
65
|
-
return args;
|
|
66
|
-
if (token.startsWith("--restore=")) {
|
|
67
|
-
leadingGlobals.push(token);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (token === "--restore") {
|
|
71
|
-
const value = args[index + 1];
|
|
72
|
-
if (optionalGlobalValueFlagConsumesNext(token, value)) {
|
|
73
|
-
leadingGlobals.push(`--restore=${value}`);
|
|
74
|
-
index += 1;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
leadingGlobals.push(token);
|
|
78
|
-
}
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (token.includes("="))
|
|
82
|
-
return args;
|
|
83
|
-
const flag = getFlagName(token);
|
|
84
|
-
if (GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES.has(flag)) {
|
|
85
|
-
leadingGlobals.push(token);
|
|
86
|
-
if (["true", "false"].includes(args[index + 1] ?? "")) {
|
|
87
|
-
leadingGlobals.push(args[index + 1]);
|
|
88
|
-
index += 1;
|
|
89
|
-
}
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (GLOBAL_VALUE_FLAGS.includes(flag)) {
|
|
93
|
-
const value = args[index + 1];
|
|
94
|
-
if (value === undefined)
|
|
95
|
-
return args;
|
|
96
|
-
// PowerShell -> .cmd drops empty argv values. Planning rejects empty
|
|
97
|
-
// caller --args; keep this defensive skip so an unexpected empty value
|
|
98
|
-
// cannot turn the next flag into its accidental value on native Windows.
|
|
99
|
-
if (value === "" && (flag === "--args" || flag === "--namespace")) {
|
|
100
|
-
index += 1;
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
leadingGlobals.push(token, value);
|
|
104
|
-
index += 1;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
return args;
|
|
108
|
-
}
|
|
109
|
-
return args;
|
|
110
|
-
}
|
|
111
37
|
export function prepareAgentBrowserSpawnArgs(args, wrapperCompatibilityUserAgent, preserveAttachedBrowserSession = false) {
|
|
112
38
|
if (preserveAttachedBrowserSession || !wrapperCompatibilityUserAgent)
|
|
113
39
|
return args;
|
|
114
40
|
return ["--args", `--user-agent=${wrapperCompatibilityUserAgent.replaceAll(/[\r\n,]/g, "")}`, ...args];
|
|
115
41
|
}
|
|
116
|
-
export function buildAgentBrowserSpawnCommand(args, platform = processPlatform) {
|
|
117
|
-
if (platform !== "win32") {
|
|
118
|
-
return { command: "agent-browser", args };
|
|
119
|
-
}
|
|
120
|
-
const invocationArgs = reorderWindowsLeadingGlobalArgs(args).map(quoteWindowsPowerShellArg).join(" ");
|
|
121
|
-
const commandLine = [
|
|
122
|
-
"$agentBrowser = Get-Command agent-browser.cmd -ErrorAction SilentlyContinue;",
|
|
123
|
-
`if (-not $agentBrowser) { [Console]::Error.WriteLine('${WINDOWS_AGENT_BROWSER_MISSING_MARKER}'); exit 127 };`,
|
|
124
|
-
`& $agentBrowser.Source ${invocationArgs}`.trimEnd(),
|
|
125
|
-
].join(" ");
|
|
126
|
-
return { command: "powershell.exe", args: ["-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", commandLine] };
|
|
127
|
-
}
|
|
128
|
-
export function isWindowsAgentBrowserCommandMissing(stderr) {
|
|
129
|
-
const normalized = stderr.toLowerCase();
|
|
130
|
-
return normalized.includes(WINDOWS_AGENT_BROWSER_MISSING_MARKER.toLowerCase()) || (normalized.includes("agent-browser.cmd") && (normalized.includes("commandnotfoundexception") ||
|
|
131
|
-
normalized.includes("not recognized as the name of a cmdlet") ||
|
|
132
|
-
normalized.includes("not recognized as an internal or external command")));
|
|
133
|
-
}
|
|
134
|
-
export function shouldCommitManagedRestoreAfterWindowsProcess(input) {
|
|
135
|
-
return !input.spawnError && !(input.exitCode !== 0 && isWindowsAgentBrowserCommandMissing(input.stderr));
|
|
136
|
-
}
|
|
137
42
|
function terminateSpawnedChild(child, signal) {
|
|
138
43
|
if (processPlatform === "win32" && child.pid) {
|
|
139
44
|
const killer = spawn("taskkill.exe", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
@@ -431,7 +336,6 @@ export async function runAgentBrowserProcess(options) {
|
|
|
431
336
|
...env,
|
|
432
337
|
...getManagedSessionRestoreProtectedEnv(managedSessionRestoreOptions, managedSessionRestoreEnv),
|
|
433
338
|
...getOwnedManagedSessionNamespaceEnv(managedSessionRestoreOptions),
|
|
434
|
-
...getWindowsExplicitDefaultNamespaceEnv(args, parentEnv.AGENT_BROWSER_NAMESPACE),
|
|
435
339
|
...ownedManagedSessionCompatibilityEnv,
|
|
436
340
|
};
|
|
437
341
|
const explicitSocketDir = processOverrides[AGENT_BROWSER_SOCKET_DIR_ENV];
|
|
@@ -536,13 +440,8 @@ export async function runAgentBrowserProcess(options) {
|
|
|
536
440
|
if (stdoutSpillHandle) {
|
|
537
441
|
await stdoutSpillHandle.close().catch(() => undefined);
|
|
538
442
|
}
|
|
539
|
-
|
|
540
|
-
if (processPlatform === "win32" && !windowsMissingBinary && !spawnError)
|
|
443
|
+
if (processPlatform === "win32" && !spawnError) {
|
|
541
444
|
agentBrowserStarted = true;
|
|
542
|
-
if (windowsMissingBinary && !spawnError) {
|
|
543
|
-
spawnError = Object.assign(new Error("spawn agent-browser ENOENT"), { code: "ENOENT" });
|
|
544
|
-
}
|
|
545
|
-
else if (processPlatform === "win32" && shouldCommitManagedRestoreAfterWindowsProcess({ exitCode, spawnError, stderr })) {
|
|
546
445
|
commitManagedSessionRestoreSuppression(managedSessionRestoreOptions);
|
|
547
446
|
}
|
|
548
447
|
if (!spawnError && stdoutSpillError) {
|
|
@@ -569,8 +468,8 @@ export async function runAgentBrowserProcess(options) {
|
|
|
569
468
|
resolve({ aborted: false, agentBrowserStarted: false, exitCode: 1, spawnError: new Error(spawnPolicyError), stderr: "", stdout: "", timedOut: false });
|
|
570
469
|
return;
|
|
571
470
|
}
|
|
572
|
-
const
|
|
573
|
-
const child =
|
|
471
|
+
const spawnBrowser = processPlatform === "win32" ? crossSpawn : spawn;
|
|
472
|
+
const child = spawnBrowser("agent-browser", prepareAgentBrowserSpawnArgs(args, ownedManagedSessionCompatibilityEnv.AGENT_BROWSER_USER_AGENT, preserveAttachedBrowserSession), {
|
|
574
473
|
cwd,
|
|
575
474
|
env: childEnv,
|
|
576
475
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isOpenNavigationCommand, isPageMutationCommand } from "../command-taxonomy.js";
|
|
2
2
|
import { isPendingRecordingArtifact } from "./artifact-manifest.js";
|
|
3
|
-
import { applySessionToNextActions, buildNextToolAction } from "./next-actions.js";
|
|
3
|
+
import { applySessionToNextActions, buildInspectOverlayStateAction, buildNextToolAction } from "./next-actions.js";
|
|
4
4
|
import { AGENT_BROWSER_RECOVERY_NEXT_ACTION_IDS, buildRecoveryNextActions, } from "./recovery-actions.js";
|
|
5
5
|
function buildArtifactAction(path) {
|
|
6
6
|
return {
|
|
@@ -223,7 +223,10 @@ export function buildAgentBrowserNextActions(options) {
|
|
|
223
223
|
}
|
|
224
224
|
break;
|
|
225
225
|
case "upstream-error":
|
|
226
|
-
if (
|
|
226
|
+
if (options.overlayBlockedClick) {
|
|
227
|
+
actions.push(buildInspectOverlayStateAction(options.sessionName));
|
|
228
|
+
}
|
|
229
|
+
else if (isOpenNavigationCommand(options.command)) {
|
|
227
230
|
actions.push(buildNextToolAction({
|
|
228
231
|
args: ["get", "url"],
|
|
229
232
|
id: "inspect-page-after-navigation-error",
|
|
@@ -17,7 +17,7 @@ async function readEnvelopeSource(options) {
|
|
|
17
17
|
throw new Error(`agent-browser output spill file could not be read: ${message}`);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
function extractEnvelopeErrorText(error) {
|
|
20
|
+
export function extractEnvelopeErrorText(error) {
|
|
21
21
|
if (typeof error === "string") {
|
|
22
22
|
return error.trim() || undefined;
|
|
23
23
|
}
|
|
@@ -152,10 +152,12 @@ export function getAgentBrowserErrorText(options) {
|
|
|
152
152
|
if (parseError)
|
|
153
153
|
return parseError;
|
|
154
154
|
if (envelope?.success === false) {
|
|
155
|
-
|
|
155
|
+
const explicitErrorText = extractEnvelopeErrorText(envelope.error);
|
|
156
|
+
if ((hasStructuredBatchStepFailure(envelope.data) || detectConfirmationRequired(envelope.data)) && explicitErrorText === undefined) {
|
|
156
157
|
return undefined;
|
|
157
158
|
}
|
|
158
|
-
const envelopeErrorText =
|
|
159
|
+
const envelopeErrorText = explicitErrorText
|
|
160
|
+
?? extractEnvelopeErrorText(typeof envelope.data === "string" ? envelope.data : isRecord(envelope.data) ? envelope.data.error : undefined);
|
|
159
161
|
if (envelopeErrorText && isUpstreamIpcReadTimeoutMessage(envelopeErrorText)) {
|
|
160
162
|
return buildUpstreamIpcReadTimeoutMessage();
|
|
161
163
|
}
|
|
@@ -44,6 +44,14 @@ export function buildNextToolAction(options) {
|
|
|
44
44
|
tool: "agent_browser",
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
+
export function buildInspectOverlayStateAction(sessionName) {
|
|
48
|
+
return buildNextToolAction({
|
|
49
|
+
args: withOptionalSessionArgs(sessionName, ["snapshot", "-i"]),
|
|
50
|
+
id: "inspect-overlay-state",
|
|
51
|
+
reason: "Refresh interactive refs and inspect whether an overlay, banner, modal, or dialog is blocking the intended click.",
|
|
52
|
+
safety: "Read-only inspection; do not blindly retry the blocked click, and use current refs from this snapshot before interacting.",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
47
55
|
export function appendUniqueAgentBrowserNextActions(target, additions) {
|
|
48
56
|
if (!additions || additions.length === 0)
|
|
49
57
|
return target;
|
|
@@ -13,7 +13,7 @@ import { extractAgentBrowserLifecycle, stringifyModelFacing } from "./common.js"
|
|
|
13
13
|
import { buildArtifactVerificationSummary, classifyPresentationSuccessCategory, manifestHasNewNoticeWorthyEntries } from "./artifacts.js";
|
|
14
14
|
import { formatBatchStepCommand, getPresentationImages, getPresentationPaths, getPresentationText, isStringArray } from "./content.js";
|
|
15
15
|
import { buildPageChangeSummary } from "./navigation.js";
|
|
16
|
-
import { appendSelectorRecoveryHint, getClipboardWritePayloadCandidates, redactClipboardPermissionErrorValue } from "./errors.js";
|
|
16
|
+
import { appendSelectorRecoveryHint, getClipboardWritePayloadCandidates, isOverlayBlockedClickError, redactClipboardPermissionErrorValue } from "./errors.js";
|
|
17
17
|
export function isAgentBrowserBatchResultArray(value) {
|
|
18
18
|
return Array.isArray(value) && value.every(isRecord);
|
|
19
19
|
}
|
|
@@ -208,6 +208,7 @@ async function buildBatchStepPresentation(options) {
|
|
|
208
208
|
command: command?.[0],
|
|
209
209
|
confirmationId: confirmationRequired?.id,
|
|
210
210
|
failureCategory,
|
|
211
|
+
overlayBlockedClick: isOverlayBlockedClickError(command?.[0], errorText, command),
|
|
211
212
|
resultCategory: "failure",
|
|
212
213
|
sessionName,
|
|
213
214
|
subcommand: command?.[1],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isOpenNavigationCommand } from "../../command-taxonomy.js";
|
|
2
|
-
import { redactSensitiveText } from "../../runtime.js";
|
|
2
|
+
import { extractUpstreamCommandTokens, redactSensitiveText } from "../../runtime.js";
|
|
3
3
|
import { buildBrowserProfileConfigRecovery } from "./browser-profile-recovery.js";
|
|
4
4
|
import { redactModelFacingText } from "./common.js";
|
|
5
5
|
import { buildAgentBrowserNextActions } from "../action-recommendations.js";
|
|
@@ -59,6 +59,13 @@ function getKeyboardPressHint(commandInfo, errorText) {
|
|
|
59
59
|
return undefined;
|
|
60
60
|
return KEYBOARD_PRESS_ERROR_HINT;
|
|
61
61
|
}
|
|
62
|
+
export function isOverlayBlockedClickError(command, errorText, args) {
|
|
63
|
+
const tokens = args ? extractUpstreamCommandTokens(args) : [];
|
|
64
|
+
const action = tokens[0] === "find"
|
|
65
|
+
? tokens[tokens[1] === "nth" ? 4 : 3] ?? "click"
|
|
66
|
+
: tokens[0] ?? command;
|
|
67
|
+
return action === "click" && errorText !== undefined && /\bis covered by\b[\s\S]*\bat its click point\b/i.test(errorText);
|
|
68
|
+
}
|
|
62
69
|
export function redactClipboardPermissionEcho(commandInfo, errorText) {
|
|
63
70
|
if (commandInfo.command !== "clipboard")
|
|
64
71
|
return errorText;
|
|
@@ -162,7 +169,7 @@ export function appendSelectorRecoveryHint(errorText) {
|
|
|
162
169
|
return `${errorText}\n\n${hint}`;
|
|
163
170
|
}
|
|
164
171
|
export function buildErrorPresentation(options) {
|
|
165
|
-
const { args, commandInfo, errorText, sessionName } = options;
|
|
172
|
+
const { args, commandInfo, errorText, presentationCommand, sessionName } = options;
|
|
166
173
|
const safeErrorText = redactModelFacingText(redactSensitiveText(redactClipboardPermissionEcho(commandInfo, errorText)));
|
|
167
174
|
const selectorHintedErrorText = appendSelectorRecoveryHint(safeErrorText);
|
|
168
175
|
const unknownCommandSuggestions = getUnknownCommandSuggestions(commandInfo.command, safeErrorText);
|
|
@@ -193,6 +200,7 @@ export function buildErrorPresentation(options) {
|
|
|
193
200
|
args,
|
|
194
201
|
command: commandInfo.command,
|
|
195
202
|
failureCategory: categoryDetails.failureCategory,
|
|
203
|
+
overlayBlockedClick: isOverlayBlockedClickError(presentationCommand ?? commandInfo.command, safeErrorText, args ?? commandInfo.commandTokens),
|
|
196
204
|
resultCategory: "failure",
|
|
197
205
|
sessionName,
|
|
198
206
|
}) ?? []),
|
|
@@ -9,7 +9,7 @@ import { applyArtifactManifest, attachInlineImage, buildArtifactVerificationSumm
|
|
|
9
9
|
import { buildBatchPresentation, isAgentBrowserBatchResultArray, redactBatchStepErrorData } from "./presentation/batch.js";
|
|
10
10
|
import { getPresentationPaths, isStringArray } from "./presentation/content.js";
|
|
11
11
|
import { buildNetworkRequestsNextActions, buildStreamNextActions, enrichStreamStatusData, formatNetworkRouteDiagnosticsText, redactPresentationData, } from "./presentation/diagnostics.js";
|
|
12
|
-
import { buildErrorPresentation } from "./presentation/errors.js";
|
|
12
|
+
import { buildErrorPresentation, isOverlayBlockedClickError } from "./presentation/errors.js";
|
|
13
13
|
import { compactLargePresentationOutput } from "./presentation/large-output.js";
|
|
14
14
|
import { buildPageChangeSummary } from "./presentation/navigation.js";
|
|
15
15
|
import { formatPresentationContentText, formatPresentationSummary } from "./presentation/registry.js";
|
|
@@ -53,7 +53,13 @@ export async function buildToolPresentation(options) {
|
|
|
53
53
|
const commandInfoWithTokens = commandInfo.commandTokens || !args ? commandInfo : { ...commandInfo, commandTokens: extractUpstreamCommandTokens(args) };
|
|
54
54
|
const presentationCommandInfo = resolvePresentationCommandInfo(commandInfoWithTokens, compiledSemanticAction);
|
|
55
55
|
if (errorText) {
|
|
56
|
-
return buildErrorPresentation({
|
|
56
|
+
return buildErrorPresentation({
|
|
57
|
+
args,
|
|
58
|
+
commandInfo,
|
|
59
|
+
errorText,
|
|
60
|
+
presentationCommand: presentationCommandInfo.command,
|
|
61
|
+
sessionName,
|
|
62
|
+
});
|
|
57
63
|
}
|
|
58
64
|
const data = enrichStreamStatusData(commandInfoWithTokens, envelope?.data);
|
|
59
65
|
const presentationData = commandInfo.command === "batch" && isAgentBrowserBatchResultArray(data)
|
|
@@ -179,6 +185,7 @@ export async function buildToolPresentation(options) {
|
|
|
179
185
|
command: presentationCommandInfo.command,
|
|
180
186
|
confirmationId: confirmationRequired?.id,
|
|
181
187
|
failureCategory: presentationWithManifest.failureCategory,
|
|
188
|
+
overlayBlockedClick: isOverlayBlockedClickError(presentationCommandInfo.command, envelope?.success === false ? presentationWithManifest.summary : undefined, args ?? presentationCommandInfo.commandTokens),
|
|
182
189
|
resultCategory: presentationWithManifest.resultCategory ?? "success",
|
|
183
190
|
savedFilePath: presentationWithManifest.savedFilePath,
|
|
184
191
|
sessionName,
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -36,7 +36,7 @@ Why:
|
|
|
36
36
|
|
|
37
37
|
The extension should:
|
|
38
38
|
- resolve `agent-browser` from `PATH`
|
|
39
|
-
- invoke it
|
|
39
|
+
- invoke it with native Node `spawn` on POSIX and `cross-spawn` on Windows. The latter handles Windows `PATH`/`PATHEXT`, `.cmd` shims and argument escaping without PowerShell or wrapper-owned command reordering. Caller commands and helper probes retain empty operands, literal doublequotes, command/subcommand adjacency and explicit default `--namespace ""`; the selected shim still owns upstream architecture selection. Keep piped stdin/stdout/stderr and terminate the full Windows shell/agent-browser process tree with `taskkill /T /F` on timeout or abort before falling back to the direct child signal. Missing commands use the spawner's `ENOENT`, not a parsed shell-error string
|
|
40
40
|
- inject `--json`
|
|
41
41
|
- complete each upstream invocation when the direct `agent-browser` child exits even if Node delays `"close"`: piped stdio can stay referenced by longer-lived descendant processes, so `runAgentBrowserProcess` watches `exit` and `close` together, leaves stdio intact during a short post-`exit` grace so normal `close` can still win, destroys streams only when the post-`exit` fallback fires, and prefers `close` codes then wrapper timeout (`124`) over signal-shaped `exit` codes (`watchSpawnedChildCompletion` / `resolveSpawnedChildExitCode` in `extensions/agent-browser/lib/process.ts`) so the tool cannot hang after the CLI process has already terminated
|
|
42
42
|
- support optional stdin only for `eval --stdin`, `batch`, `auth save --password-stdin`, and wrapper-generated `batch` stdin from top-level `job`, `qa`, `sourceLookup`, or `networkSourceLookup`, rejecting other command/stdin combinations before launch; top-level `electron` never accepts caller `stdin` (see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#electron))
|
|
@@ -337,7 +337,7 @@ Treat `@eN`, `eN`, and `ref=eN` selector refs as page-scoped. Ref-looking fill/t
|
|
|
337
337
|
|
|
338
338
|
A successful `click` result means upstream reported a target, not that the app definitely handled the event. For top-level non-Electron direct clicks on `xpath=` targets and eligible current `@e…` refs, the wrapper installs a bounded target-specific DOM-event probe when it can; when upstream reports success but no trusted event reaches the resolved target, it fails the tool and exposes `details.clickDispatch` plus a `Click dispatch diagnostic` line with explicit retry/inspect next actions (no in-page click replay). Raw `find … click` locator calls are not probed because the wrapper has no concrete element before upstream resolves the locator, and document-level probes can falsely fail frame-scoped clicks. Direct `@e…` click probes are role-gated to current snapshot refs whose accessible role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`, with a unique role/name in both the saved snapshot and the live candidates. Duplicate-name refs pass through without a probe because their old ordinal does not prove target identity. If the probe evidence shows the target is outside a nested scroll container or viewport, `details.clickDispatch.scrollContainer` and `scroll-target-into-view-after-dispatch-miss` point to `scrollintoview <target>` before retry. When the workflow depends on a mutation, use `details.pageChangeSummary`, a wait, URL/text extraction, or a fresh `snapshot -i` before trusting the state; if nothing changed, retry with a current visible ref or stable selector and report the workflow issue. For static local fixtures or debugging where the user explicitly accepts scripted activation, `eval --stdin` can call `document.querySelector(...).click()` to exercise inline handlers and app code; treat that as an untrusted programmatic event, not as evidence that CDP/user-like clicking works. Respect explicit user stop boundaries yourself: if the user says to stop before a final order, post, purchase, or submit action, gather evidence from that page and do not click the final action or use scripted activation to bypass the stop. The wrapper does not infer broad business intent from prompt text; `details.promptGuard` is reserved for concrete artifact-before-close checks. `press`, `key`, `keydown`, and `keyup` accept exactly one key token; focus or click the target first, then run `press Enter` or another single-key command.
|
|
339
339
|
|
|
340
|
-
Successful `snapshot -i` results can also surface `Possible overlay blockers` when their own refs already show dialog/alertdialog context plus close/dismiss controls, so agents can detect likely obstruction before clicking. When a **top-level** `@e…`/`ref=` click succeeds (not a `click` hidden inside a `batch`/`job` tool call—the unified command must be `click`), the upstream payload includes `data.clicked`, no `details.clickDispatch` diagnostic fired for the same result, and the wrapper sees `details.navigationSummary.url` unchanged after the same normalization it uses for ref guards (**`#fragment` ignored**), it may run one extra `snapshot -i` and surface `Possible overlay blockers` plus `details.overlayBlockers` (`candidates`, `summary`, and a `snapshot` map that can refresh `refSnapshot`) when that snapshot shows strong modal context (`dialog` / `alertdialog`) **and** up to three close/dismiss-like controls; page-wide words such as privacy, sign in, or banner alone do not trigger it. The URL check compares the session’s prior pinned tab target to `details.navigationSummary.url`. CSS selector clicks do not run this overlay probe. The diagnostic is skipped if the wrapper already applied tab-focus correction or about-blank recovery on that result. Appended `inspect-overlay-state` / `try-overlay-blocker-candidate-*` entries in `details.nextActions` preserve namespace/session context (`--namespace <namespace> --session <name>` when namespaced, otherwise `--session <name>` when the session is named), same as other session-scoped follow-ups. Treat `inspect-overlay-state` as the safe first follow-up; only use a `try-overlay-blocker-candidate-*` next action when the candidate is clearly the control you intend to close.
|
|
340
|
+
Successful `snapshot -i` results can also surface `Possible overlay blockers` when their own refs already show dialog/alertdialog context plus close/dismiss controls, so agents can detect likely obstruction before clicking. When a **top-level** `@e…`/`ref=` click succeeds (not a `click` hidden inside a `batch`/`job` tool call—the unified command must be `click`), the upstream payload includes `data.clicked`, no `details.clickDispatch` diagnostic fired for the same result, and the wrapper sees `details.navigationSummary.url` unchanged after the same normalization it uses for ref guards (**`#fragment` ignored**), it may run one extra `snapshot -i` and surface `Possible overlay blockers` plus `details.overlayBlockers` (`candidates`, `summary`, and a `snapshot` map that can refresh `refSnapshot`) when that snapshot shows strong modal context (`dialog` / `alertdialog`) **and** up to three close/dismiss-like controls; page-wide words such as privacy, sign in, or banner alone do not trigger it. The URL check compares the session’s prior pinned tab target to `details.navigationSummary.url`. CSS selector clicks do not run this overlay probe. The diagnostic is skipped if the wrapper already applied tab-focus correction or about-blank recovery on that result. Appended `inspect-overlay-state` / `try-overlay-blocker-candidate-*` entries in `details.nextActions` preserve namespace/session context (`--namespace <namespace> --session <name>` when namespaced, otherwise `--session <name>` when the session is named), same as other session-scoped follow-ups. Treat `inspect-overlay-state` as the safe first follow-up; only use a `try-overlay-blocker-candidate-*` next action when the candidate is clearly the control you intend to close. A click that upstream rejects because another element covers the target's click point (`is covered by` … `at its click point`) remains `failureCategory: "upstream-error"`, but receives the same session-aware `inspect-overlay-state` snapshot action. This includes direct `click`, `semanticAction` click, raw `find` clicks (including `nth` and omitted default-click actions), and failed `batch`/`job` rows. That failed-click path does not retry the original click or synthesize a dismiss candidate before a fresh snapshot provides evidence; it is separate from silent input-dispatch failures.
|
|
341
341
|
|
|
342
342
|
### Extract page data
|
|
343
343
|
|
|
@@ -859,6 +859,8 @@ Current upstream still does not parse `wait <selector> --state hidden` / `wait <
|
|
|
859
859
|
| `pushstate <url>` | Perform SPA client-side navigation; detects Next.js router pushes and falls back to history navigation events. |
|
|
860
860
|
| `removeinitscript <id>` | Remove an init script registered through upstream init-script mechanisms. |
|
|
861
861
|
|
|
862
|
+
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.
|
|
863
|
+
|
|
862
864
|
When these diagnostic commands are invoked through the native `agent_browser` tool, structured console, page-error, React, Web Vitals, and SPA outputs render as compact summaries when possible, with large outputs previewed and spilled instead of dumped into context. Large outputs are previewed with a `Full output path:` spill file instead of dumping the entire payload into context. Artifact-producing commands such as `network har stop`, `diff screenshot`, `trace stop`, `profiler stop`, and `record stop` report `details.artifacts[]` plus `details.artifactVerification`; `record start` / `record restart` are reported as pending until `record stop` completes. For video workflows, keep `ffmpeg` on `PATH` first; on macOS with Homebrew, `brew install ffmpeg` or `brew install ffmpeg-full` is sufficient. Successful `record start` / `record restart` results warn early with `details.recordingDependencyWarning` when the wrapper cannot find `ffmpeg`, so fix PATH before `record stop` instead of discovering the missing encoder after the capture. The README install section keeps the concise external-dependency list for maximal extension use.
|
|
863
865
|
|
|
864
866
|
Long-running or lifecycle commands should be explicitly paired with cleanup calls: `stream enable` → `stream disable`, `dashboard start` → `dashboard stop`, `trace start` → `trace stop`, `profiler start` → `profiler stop`, and `record start` → `record stop`. The wrapper keeps each subprocess bounded by its normal timeout; it does not keep an interactive `chat` REPL open, so prefer `chat <message>` with `--model` or `AI_GATEWAY_MODEL` for single-shot AI use.
|
package/docs/SUPPORT_MATRIX.md
CHANGED
|
@@ -79,6 +79,8 @@ Cold URL reopen is covered by `test/agent-browser.cold-resume.test.ts`, the daem
|
|
|
79
79
|
|
|
80
80
|
Batch/ref fidelity (#122/#123/#125/#126): tab recovery uses verified native selection under existing session serialization without reconstructing argv/stdin or changing caller batch control flow. Selector-only stale-ref checks cover `@eN`, `eN`, and `ref=eN`; literal operands and keyboard/mouse data remain native. `test/agent-browser.batch-fidelity.test.ts` runs paired pinned/unpinned real-upstream fixtures, including mixed failures, header/timeout flags, ignored stdin, current/stale refs, failed/wrong-target selection, sessionless commands, and explicit connection/state recovery with failed-batch negatives. It runs in `npm run verify -- real-upstream`; deterministic selector/shape checks also run in the default suite.
|
|
81
81
|
|
|
82
|
+
Overlay recovery (RQ-0073, #147) recommends only `inspect-overlay-state` after an upstream covered-click rejection, preserving `upstream-error` and the exact session/namespace. Direct, semantic, raw `find` (including `nth` and default-click), and failed batch/job paths share the matcher; hover and generic errors do not qualify. `test/agent-browser.overlay-click-recovery.test.ts` covers presentation and error envelopes, `test/agent-browser.pi-pipeline.test.ts` covers persisted errors and parseable JSON, and the opt-in `test/agent-browser.overlay-real-upstream.test.ts` checks actual covered clicks, executable inspection, and unchanged target/blocker click counters. This is not a fix for silent input dispatch (#62).
|
|
83
|
+
|
|
82
84
|
## Verification evidence
|
|
83
85
|
|
|
84
86
|
Re-run the gates below before each release; this table records what the closure audit exercised. The recommended 0.36.0 rebaseline passed the local pre-PR, real-upstream, and deterministic dogfood gates on 2026-09-01. The 0.35.0 runtime floor was last validated locally on 2026-08-26; the full platform matrix remains setup-blocked by disabled macOS SSH and missing Parallels `prlctl`.
|
|
@@ -87,8 +89,9 @@ Re-run the gates below before each release; this table records what the closure
|
|
|
87
89
|
| --- | --- | --- |
|
|
88
90
|
| Default local gate | `npm run verify` checks generated playbook drift, clean-builds generated `dist/`, runs `tsc --noEmit`, unit/fake tests, generated command-reference blocks, and live command-reference sampling. | **Current for 0.36.0:** pass on 2026-09-01; 779 tests passed, two opt-in real-upstream tests skipped, and build/typecheck/docs/live command-reference gates passed. |
|
|
89
91
|
| Pre-PR local gate | `npm run verify -- pre-pr` composes the default gate with package-content verification. Use before larger local handoffs or PR-ready claims when lifecycle/platform/live dogfood cost is not warranted. | **Current for 0.36.0 / package 0.6.2:** pass on 2026-09-01; 779 tests passed, two opt-in tests skipped, and the 127-file tarball verified. |
|
|
92
|
+
| Windows argument transport (#102/#131/#141) | `node --import tsx --test test/agent-browser.windows-argv.test.ts` exercises the actual host subprocess with independent expected argv, empty fill/launch/namespace values, literal quotes, Unicode/spaces, stdin, child-`PATH` custom shims and missing-command/nonzero-exit truth. It also runs in the default and `platform-target` gates. Windows uses `cross-spawn`; POSIX keeps native Node `spawn`. | The old PowerShell Legacy path dropped empty operands and split ordinary doublequoted fill text; the quoted-empty-only proposal fixed the former but left the latter broken. Native macOS and Ubuntu validate the unchanged POSIX subprocess path. Native Windows/`cmd.exe` and affected-host managed open → snapshot → close retests remain unrun under the task-specific waiver; neither Linux PowerShell diagnostics nor dependency tests are reported as native Windows proof. |
|
|
90
93
|
| Real upstream contract | `npm run verify -- real-upstream` runs the localhost fixture matrix against a real stable `agent-browser` at or above the configured floor. | **Current for recommended 0.36.0:** pass on 2026-09-01 (2/2 tests), including WebMCP and `--no-webmcp`; the 0.35.0 floor last passed on 2026-08-26 (2/2 tests). |
|
|
91
|
-
| Packaged Pi smoke | `npm run verify -- package-pi` validates package contents, loads the packaged `agent_browser` tool without requiring optional Brave config, and executes fake-upstream `--version`. | **Current for package 0.6.2 / Pi 0.84.4:** pass on 2026-09-01; the 127-file tarball loaded exactly one packaged `agent_browser` and executed `--version`. |
|
|
94
|
+
| Packaged Pi smoke | `npm run verify -- package-pi` validates package contents, installs the extracted tarball's runtime dependencies without lifecycle scripts or host peers, loads the packaged `agent_browser` tool without requiring optional Brave config, and executes fake-upstream `--version`. | **Current for package 0.6.2 / Pi 0.84.4:** pass on 2026-09-01; the 127-file tarball loaded exactly one packaged `agent_browser` and executed `--version`. |
|
|
92
95
|
| Startup profile | `npm run verify -- startup-profile --samples <n>` clean-builds generated `dist/`, records direct package entrypoint import/factory timing in fresh Node processes, and writes `.artifacts/startup-profile/latest.json`. It must not launch Pi, tmux, mise, npm, browsers, or `agent-browser`; full Pi TUI ready-prompt profiling is intentionally excluded after it proved too invasive for routine verification. Run this opt-in evidence when package layout, the compiled entrypoint, top-level imports, schema registration, or prompt/config startup logic changes. | **Current for compiled 0.6.2 entrypoint:** pass on 2026-09-01 with three samples; median 104.9 ms and max 114.5 ms, below the 250 ms budget. |
|
|
93
96
|
| Deterministic dogfood smoke | `npm run verify -- dogfood` clean-builds, then `scripts/verify-agent-browser-dogfood.ts` drives the native wrapper against a loopback HTTP fixture through top-level `script` conditional aggregation/cleanup, `qa`, `semanticAction`, constrained `job`, screenshot artifact verification, and session close with the real `agent-browser` on `PATH`. | **Current for 0.36.0 / package 0.6.2 / Pi 0.84.4:** pass on 2026-09-01; QA, script branching/cleanup, fresh/current opens, semantic clicks, job screenshot verification, and close all passed. |
|
|
94
97
|
| WebGPU and restore autosave | Live 0.31.2 feature probes validate the new upstream paths independently from ordinary browser dogfood. | **macOS:** `doctor --webgpu` passed Apple Metal render/readback and headless red-pixel screenshot checks; the official Hello Triangle sample exposed the Metal adapter and produced a verified non-black screenshot. **Ubuntu image:** `doctor --webgpu --headed --json` passed SwiftShader render/readback and red-pixel screenshot checks with the baked Vulkan/Mesa/Xvfb dependencies. **Restore autosave:** an idle headless page timer changed localStorage and the namespaced restore file contained the new value before close; test state was removed. Wrapper-owned headed launches default periodic autosave off to avoid upstream 0.33.2 visible temporary collector tabs; native close still saves, direct window close can lose newer state because headed browsers are exempt from idle shutdown, and explicit `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` opts in at daemon launch; its effective value persists across resume, and changing a running wrapper-owned headed daemon in either direction requires close plus a fresh launch. On 2026-08-06, a headed checkout dogfood run crossed `react.dev` → `example.com` → `react.dev`, idled past the autosave interval while recording, showed no temporary page switch in frame/contact-sheet review, and closed both sessions successfully; disposable evidence was removed. **Windows interactive desktop:** post-release validation on 2026-07-15 used a disposable clone of snapshot `57cc3c0d-7d7c-4a4c-9c57-8270d4174679`, a logged-in console session, `agent-browser 0.31.2`, and Edge 150. The headed doctor WebGPU render/readback subcheck passed in 0.99s and its decoded screenshot subcheck passed with `rgb(255,0,0)`; a separate `--webgpu --headed` launch opened the proof page and the Parallels console capture visibly recorded the red triangle (553,500 bright-red pixels; SHA-256 `f5a28f5336cbdfeb0ff557af9425458bddc6b266d3cc6b946de7b101a0b43288`). The full doctor remained nonzero only for the unrelated absence of a separately installed Chrome binary; the explicit Edge executable completed the launch and both WebGPU probes. Local evidence is under `.artifacts/windows-webgpu-interactive/20260715T150903Z/summary.json`; the disposable clone and temporary account were deleted afterward. |
|
|
@@ -105,7 +108,7 @@ Runtime floor note: package metadata keeps optional Pi core package peer ranges
|
|
|
105
108
|
| Baseline section | Baseline items | Documentation | Runtime handling | Test coverage | Validation status |
|
|
106
109
|
| --- | --- | --- | --- | --- | --- |
|
|
107
110
|
| Built-in skills | 19 canonical tokens from baseline section `skills`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#built-in-skills). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#built-in-skills), generated baseline block, README proof section, release docs. | `needsManagedSession` keeps read-only skills inspection sessionless while preserving thin upstream passthrough; upstream `@agent-browser/sandbox` remains external package guidance, not a bundled wrapper dependency. | Runtime and extension-validation skills/provider matrix; real-upstream inspection/skills group. | Supported. |
|
|
108
|
-
| Core page, element, navigation, and extraction commands | 82 canonical tokens from baseline section `core-commands`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#core-page-and-element-commands). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#core-page-and-element-commands), [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md), README quick start. | Thin passthrough with wrapper-owned JSON/session planning, ref guidance, artifact verification, page-change summaries, click-dispatch diagnostics, deterministic document scroll before wheel/no-op diagnostics, shorthand compilers, and redaction. | Real-upstream core matrix (including snapshot-ref select) plus fake core matrix for passthrough, ordering, diagnostics, and compiler validation. | Supported. Upstream semantics remain upstream-owned. |
|
|
111
|
+
| Core page, element, navigation, and extraction commands | 82 canonical tokens from baseline section `core-commands`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#core-page-and-element-commands). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#core-page-and-element-commands), [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md), README quick start. | Thin passthrough with wrapper-owned JSON/session planning, ref guidance, artifact verification, page-change summaries, click-dispatch diagnostics, inspection-only recovery for upstream overlay-blocked clicks, deterministic document scroll before wheel/no-op diagnostics, shorthand compilers, and redaction. | Real-upstream core matrix (including snapshot-ref select) plus fake core matrix for passthrough, ordering, diagnostics, and compiler validation. | Supported. Upstream semantics remain upstream-owned. |
|
|
109
112
|
| Sessions, state, tabs, frames, dialogs, and windows | 28 canonical tokens from baseline section `state-tabs-frames-dialogs`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#session-state-frames-dialogs-windows-and-inspection-commands). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#session-state-frames-dialogs-windows-and-inspection-commands), stateful workflow notes, [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details). | Stateful summaries redact credentials while preserving restore identifiers and every session/state list row; explicit targets and paths pass through. Runtime retains state artifact verification, sessionless local command planning, automatic managed restore, tab target pinning, active-target refresh after tab close, and close alias cleanup. | Extension tab/ref tests, real-upstream stable-id/label tab lifecycle, runtime session/resume tests, presentation redaction tests, lifecycle harness. | Supported. External profile/auth state remains operator-owned. |
|
|
110
113
|
| Network, storage, artifacts, diagnostics, and performance | 57 canonical tokens from baseline section `network-storage-artifacts-diagnostics`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#page-state-finding-mouse-settings-network-and-storage). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#page-state-finding-mouse-settings-network-and-storage), diagnostic sections, [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details). | Thin passthrough plus compact diagnostics, route-mock warnings, useful-but-redacted storage output, stream idempotency normalization, artifact metadata, missing-ffmpeg warnings, sensitive-data redaction, timeout bounds, and cleanup-pair guidance. | Fake non-core matrix and safe real-upstream coverage for network/HAR, diff, trace/profiler, console/errors/highlight, stream, vitals, and React missing-renderer. | Supported. Environment-sensitive operations need suitable local/browser state. |
|
|
111
114
|
| Batch, auth, confirmations, setup, dashboard, devices, and AI commands | 36 canonical tokens from baseline section `batch-auth-setup-ai`; see [`scripts/agent-browser-capability-baseline.mjs`](../scripts/agent-browser-capability-baseline.mjs) and generated [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#batch-auth-confirmations-sessions-chat-dashboard-devices-and-setup). | [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#batch-auth-confirmations-sessions-chat-dashboard-devices-and-setup), README security notes, release docs. | Native-tool batch stdin, generated `job`/`qa`/lookup batch plans, auth/confirmation redaction, sessionless local auth/setup/dashboard/doctor/plugin planning, upgrade-only native text normalization with error precedence and explicit-JSON results, plugin list/show JSON envelope normalization, bare-`mcp` validation with `mcp --help` preserved, timeout/cleanup guidance. | Parser/runtime plugin and MCP unit coverage; fake-upstream plugin list/show and MCP help/blocking coverage; registered upgrade text/success/nonzero/timeout/abort/JSON controls in `test/agent-browser.upgrade-output.test.ts`; real-upstream plugin list shape probe; structured input-mode tests. | Supported. Interactive side-effecting setup/auth/chat remains upstream-owned. `plugin` is local/sessionless; `mcp` is external-client-only except help; `auth login --credential-provider` resolves credentials via a plugin; `install --with-deps` failures remain upstream-owned. |
|
package/docs/TOOL_CONTRACT.md
CHANGED
|
@@ -201,7 +201,7 @@ Upstream 0.35.2 adds `dashboard start --allowed-origins <origins>` and `AGENT_BR
|
|
|
201
201
|
- On dashboards with nested scroll containers, verify scroll with a screenshot or fresh snapshot -i; if the viewport did not move, details.data.scrolled may be false/noMovement true and you should prefer scrollintoview <@ref> or target the actual scrollable region with scroll <selector> <dir> [px|percent]. For native selects, use select <selector> <value...> (or semanticAction/job select) instead of clicking option refs; for custom comboboxes, a click/semanticAction may only focus the field, so re-snapshot and use keyboard type <text> for focused input, press ArrowDown or press Enter, or visible option refs. Raw type requires both <selector> and <text>.
|
|
202
202
|
- When using eval --stdin, scope checks and actions to the target element or route whenever possible instead of relying on broad page-wide text heuristics.
|
|
203
203
|
- When using eval --stdin for extraction, pass the JavaScript through the native tool stdin field, not as an extra args token after --stdin, and return the value you want instead of relying on console.log as the primary result channel. Prefer plain expressions like ({ title: document.title }) or explicitly invoked functions like (() => ({ title: document.title }))(); use outputPath when the eval/get/snapshot data should be saved as a durable local file, but never reuse a screenshot, download, recording, or other browser artifact destination as outputPath. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. Local file pages and caller-selected output paths are supported when upstream allows them. If get text on a broad CSS selector surfaces details.selectorTextVisibility or selectorTextVisibilityAll, prefer a visible @ref, a more specific selector, or the inspect-visible-text-candidates nextAction over hidden tab content.
|
|
204
|
-
- When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.
|
|
204
|
+
- When details.pageChangeSummary is present, use changeType and summary as a compact signal for navigation, DOM mutation, confirmations, or artifacts; when nextActionIds is set, match those ids to entries in details.nextActions (or per-step nextActions inside batch) for concrete follow-up payloads instead of inferring from prose alone. If details.clickDispatch reports a click-dispatch miss, refresh/inspect/retry the real click first; for static local fixtures only, an explicit eval --stdin programmatic .click() can exercise app handlers, but treat it as an untrusted scripted workaround and never use it to bypass stop-before-submit/order/purchase boundaries. If an upstream click failure says the target is covered by another element at the target's click point, use the inspect-overlay-state nextAction to refresh refs and inspect the blocker before deciding whether to retry; do not blindly repeat the blocked click. If a no-navigation click surfaces details.overlayBlockers, inspect the fresh snapshot evidence before using a close/dismiss candidate nextAction; ordinary page chrome without dialog/alertdialog evidence should not trigger this diagnostic.
|
|
205
205
|
- 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
206
|
- 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
207
|
- 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.
|
|
@@ -789,7 +789,7 @@ Ref preflight details (command taxonomy in `extensions/agent-browser/lib/command
|
|
|
789
789
|
|
|
790
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 `redactStructuredPresentationValue`, 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
791
|
|
|
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`; 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.
|
|
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.
|
|
793
793
|
|
|
794
794
|
**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
795
|
|
|
@@ -905,6 +905,8 @@ Additional structured fields can appear when relevant:
|
|
|
905
905
|
- `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
906
|
- `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
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 WebM 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 definitive `No recording in progress` stop failure, direct or nested in a batch, retires stale reservation state at that ordered step instead of recommending the same stop again; 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.
|
|
908
|
+
|
|
909
|
+
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.
|
|
908
910
|
- `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.
|
|
909
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` represents an earlier unfinalized pending recording as `status: "missing"` / `subcommand: "close-abandoned"`, clears its stop action, and 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. 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.
|
|
910
912
|
- `artifactVerification` for a normalized verification summary on the unified result and on each successful `batchSteps[]` row (failed batch steps omit artifact rows). 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.
|
|
@@ -970,7 +972,7 @@ If `agent-browser` is not on `PATH`, fail with a message that:
|
|
|
970
972
|
- when an unnamed `sessionMode: "fresh"` launch succeeds, make it the new extension-managed session so later default calls keep using it
|
|
971
973
|
- 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`)
|
|
972
974
|
- 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
|
|
973
|
-
- 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.
|
|
975
|
+
- 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`.
|
|
974
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.
|
|
975
977
|
- pass explicit `--profile` straight through to upstream `agent-browser`; no profile-cloning or isolation layer is added in v1
|
|
976
978
|
<!-- agent-browser-playbook:start wrapper-tab-recovery -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-agent-browser-native",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "pi extension that exposes agent-browser as a native tool for browser automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Mitch Fultz (https://github.com/fitchmultz)",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"@earendil-works/pi-ai": "0.84.0",
|
|
69
69
|
"@earendil-works/pi-coding-agent": "0.84.0",
|
|
70
70
|
"@earendil-works/pi-tui": "0.84.0",
|
|
71
|
+
"@types/cross-spawn": "6.0.6",
|
|
71
72
|
"@types/node": "^25.9.3",
|
|
72
73
|
"tsx": "^4.21.0",
|
|
73
74
|
"typebox": "^1.1.38",
|
|
@@ -111,5 +112,8 @@
|
|
|
111
112
|
"typebox": {
|
|
112
113
|
"optional": true
|
|
113
114
|
}
|
|
115
|
+
},
|
|
116
|
+
"dependencies": {
|
|
117
|
+
"cross-spawn": "7.0.6"
|
|
114
118
|
}
|
|
115
119
|
}
|