pi-agent-browser-native 0.6.9 → 0.6.10
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 +13 -13
- package/dist/extensions/agent-browser/index.js +8 -21
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +6 -7
- package/dist/extensions/agent-browser/lib/argv-grammar.js +6 -0
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +4 -8
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +15 -2
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +2 -2
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +3 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +6 -14
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +11 -25
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +4 -4
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +13 -17
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +5 -11
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +2 -2
- package/dist/extensions/agent-browser/lib/playbook.js +4 -4
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +17 -29
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +5 -5
- package/dist/extensions/agent-browser/lib/session-page-state.js +1 -1
- package/dist/scripts/agent-browser-target.mjs +1 -1
- package/docs/ARCHITECTURE.md +4 -3
- package/docs/COMMAND_REFERENCE.md +34 -18
- package/docs/RELEASE.md +6 -4
- package/docs/SUPPORT_MATRIX.md +18 -14
- package/docs/TOOL_CONTRACT.md +11 -9
- package/package.json +1 -1
- package/scripts/agent-browser-capability-baseline.mjs +10 -3
- package/scripts/agent-browser-target.mjs +1 -1
|
@@ -73,21 +73,19 @@ async function ensureArtifactParentDirectory(commandTokens, cwd) {
|
|
|
73
73
|
return;
|
|
74
74
|
await mkdir(dirname(resolve(cwd, requestedPath)), { recursive: true });
|
|
75
75
|
}
|
|
76
|
-
async function normalizeScreenshotPathInTokens(commandTokens, cwd) {
|
|
77
|
-
|
|
78
|
-
const projection = projectUpstreamGlobalFlags(
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
76
|
+
async function normalizeScreenshotPathInTokens(commandTokens, cwd, batchStep = false) {
|
|
77
|
+
// Native batch rows skip outer CLI global-flag cleanup.
|
|
78
|
+
const projection = batchStep ? undefined : projectUpstreamGlobalFlags(commandTokens);
|
|
79
|
+
const pathIndex = getScreenshotPathTokenIndex(projection?.tokens ?? commandTokens);
|
|
80
|
+
const screenshotPathTokenIndex = pathIndex === undefined ? undefined : projection ? projection.indices[pathIndex] : pathIndex;
|
|
81
|
+
if (screenshotPathTokenIndex === undefined)
|
|
82
82
|
return { tokens: commandTokens };
|
|
83
|
-
}
|
|
84
|
-
const screenshotPathTokenIndex = commandTokens.length - scopedCommandTokens.length + scopedPathTokenIndex;
|
|
85
83
|
const requestedPath = commandTokens[screenshotPathTokenIndex];
|
|
86
84
|
const absolutePath = resolve(cwd, requestedPath);
|
|
87
85
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
88
86
|
const tokens = [...commandTokens];
|
|
89
87
|
tokens[screenshotPathTokenIndex] = absolutePath;
|
|
90
|
-
const terminatorIndex = tokens.indexOf("--");
|
|
88
|
+
const terminatorIndex = batchStep ? -1 : tokens.indexOf("--");
|
|
91
89
|
if (terminatorIndex >= 0) {
|
|
92
90
|
tokens.splice(terminatorIndex, 1);
|
|
93
91
|
}
|
|
@@ -110,13 +108,12 @@ async function prepareBatchScreenshotPaths(args, stdin, cwd) {
|
|
|
110
108
|
// prepare parent directories for the rows that will run and skip stdin
|
|
111
109
|
// preparation (no directories for never-executed rows).
|
|
112
110
|
for (const step of argumentSteps) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (stepTokens[0] === "screenshot") {
|
|
111
|
+
await ensureArtifactParentDirectory(step, cwd);
|
|
112
|
+
if (step[0] === "screenshot") {
|
|
116
113
|
// Reuse the screenshot path resolution for its parent-directory side
|
|
117
114
|
// effect only: raw strings are never rewritten, so the normalized
|
|
118
115
|
// tokens and path request are deliberately discarded.
|
|
119
|
-
await normalizeScreenshotPathInTokens(step, cwd);
|
|
116
|
+
await normalizeScreenshotPathInTokens(step, cwd, true);
|
|
120
117
|
}
|
|
121
118
|
}
|
|
122
119
|
return undefined;
|
|
@@ -134,12 +131,11 @@ async function prepareBatchScreenshotPaths(args, stdin, cwd) {
|
|
|
134
131
|
if (!Array.isArray(step) || !step.every((item) => typeof item === "string")) {
|
|
135
132
|
return step;
|
|
136
133
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (upstreamStep[0] !== "screenshot") {
|
|
134
|
+
await ensureArtifactParentDirectory(step, cwd);
|
|
135
|
+
if (step[0] !== "screenshot") {
|
|
140
136
|
return step;
|
|
141
137
|
}
|
|
142
|
-
const normalized = await normalizeScreenshotPathInTokens(step, cwd);
|
|
138
|
+
const normalized = await normalizeScreenshotPathInTokens(step, cwd, true);
|
|
143
139
|
batchScreenshotPathRequests[index] = normalized.request;
|
|
144
140
|
if (normalized.request) {
|
|
145
141
|
changed = true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
2
|
import { parseArgvDescriptor } from "../../argv-descriptor.js";
|
|
3
3
|
import { needsManagedSession } from "../../command-policy.js";
|
|
4
|
-
import { getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace } from "../../argv-grammar.js";
|
|
4
|
+
import { deleteIdentityKeysInNamespace, getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace } from "../../argv-grammar.js";
|
|
5
5
|
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "../../batch-lifecycle.js";
|
|
6
6
|
import { isCloseAllCommand, isCloseCommand, isOpenNavigationCommand, isRecordPageTransitionCommand, isUnverifiedPageTransitionCommand, isWindowOrDiffPageTransitionCommand } from "../../command-taxonomy.js";
|
|
7
7
|
import { OPEN_RESULT_TAB_CORRECTION_FLAGS } from "../../launch-scoped-flags.js";
|
|
@@ -108,12 +108,6 @@ function batchStartedManagedBrowser(data) {
|
|
|
108
108
|
function withoutNamespaceEntries(entries, namespace) {
|
|
109
109
|
return new Map([...entries].filter(([key]) => !isAgentBrowserSessionIdentityKeyInNamespace(key, namespace)));
|
|
110
110
|
}
|
|
111
|
-
function deleteNamespaceEntries(entries, namespace) {
|
|
112
|
-
for (const key of entries.keys()) {
|
|
113
|
-
if (isAgentBrowserSessionIdentityKeyInNamespace(key, namespace))
|
|
114
|
-
entries.delete(key);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
111
|
function setNetworkRouteState(options) {
|
|
118
112
|
if (!options.sessionName)
|
|
119
113
|
return options.routesBySession;
|
|
@@ -223,8 +217,8 @@ export async function processBrowserOutput(input) {
|
|
|
223
217
|
}
|
|
224
218
|
if (closeAllApplied) {
|
|
225
219
|
networkRoutesBySession = withoutNamespaceEntries(networkRoutesBySession, prepared.executionPlan.namespace);
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
deleteIdentityKeysInNamespace(state.attachedSessionKeys, prepared.executionPlan.namespace);
|
|
221
|
+
deleteIdentityKeysInNamespace(traceOwners, prepared.executionPlan.namespace);
|
|
228
222
|
sessionPageState.clearNamespace(prepared.executionPlan.namespace);
|
|
229
223
|
const retainedSessionKey = nestedBatchRemainsActive ? sessionStateKey : undefined;
|
|
230
224
|
for (const [key, owner] of state.ownedManagedSessions) {
|
|
@@ -370,7 +364,7 @@ export async function processBrowserOutput(input) {
|
|
|
370
364
|
let fillVerificationDiagnostic;
|
|
371
365
|
let selectorTextVisibilityDiagnostics = [];
|
|
372
366
|
let electronBroadGetTextScopeDiagnostics = [];
|
|
373
|
-
const timeoutPartialProgress = processResult.timedOut ? await collectTimeoutPartialProgress({
|
|
367
|
+
const timeoutPartialProgress = processResult.timedOut ? await collectTimeoutPartialProgress({ commandTokens: prepared.commandTokens, compiledJob: prepared.compiledJob, cwd, namespace: prepared.executionPlan.namespace, sessionName: prepared.executionPlan.sessionName, stdin: prepared.runtimeToolStdin }) : undefined;
|
|
374
368
|
if (!currentSessionTabTarget && timeoutPartialProgress?.currentPage?.source === "live") {
|
|
375
369
|
currentSessionTabTarget = normalizeSessionTabTarget(timeoutPartialProgress.currentPage);
|
|
376
370
|
}
|
|
@@ -763,7 +757,7 @@ export async function processBrowserOutput(input) {
|
|
|
763
757
|
? presentation.batchSteps?.some((step) => isRecordPageTransitionCommand(extractUpstreamCommandTokens(step.command ?? [])))
|
|
764
758
|
: isRecordPageTransitionCommand(prepared.commandTokens);
|
|
765
759
|
const recordingPageWarning = processResult.agentBrowserStarted && !prepared.executionPlan.plainTextInspection && recordingTransitionReached
|
|
766
|
-
? "Page state: this
|
|
760
|
+
? "Page state: this wrapper conservatively invalidates earlier refs after recording starts and URL-bearing restarts. Take a fresh snapshot before continuing; this does not prove the page changed."
|
|
767
761
|
: undefined;
|
|
768
762
|
const sessionWarning = electronPostCommandHealth ? formatElectronPostCommandHealthText(electronPostCommandHealth) : electronSessionMismatch ? formatElectronSessionMismatchText(electronSessionMismatch) : aboutBlankSessionMismatch ? buildAboutBlankWarning(aboutBlankSessionMismatch) : undefined;
|
|
769
763
|
const warningText = [sessionWarning, recordingPageWarning].filter(Boolean).join("\n\n") || undefined;
|
|
@@ -7,7 +7,7 @@ import { buildAgentBrowserNextActions } from "../../results/action-recommendatio
|
|
|
7
7
|
import { parseAgentBrowserEnvelope } from "../../results/envelope.js";
|
|
8
8
|
import { buildNextToolAction, withOptionalNamespaceArgs, withOptionalSessionArgs } from "../../results/next-actions.js";
|
|
9
9
|
import { getSessionPageStateKey, isAboutBlankUrl, normalizeComparableUrl, targetsMatch, } from "../../session-page-state.js";
|
|
10
|
-
import { isCloseCommand, isElectronPostCommandHealthCommand, isNavigationObservableCommandName, isOpenNavigationCommand, isRefGuardedCommand, isRefInvalidatingBatchCommand,
|
|
10
|
+
import { getRecordCommandOperands, isCloseCommand, isElectronPostCommandHealthCommand, isNavigationObservableCommandName, isOpenNavigationCommand, isRefGuardedCommand, isRefInvalidatingBatchCommand, isSessionTabPinningExcludedCommand, isSessionTabPostCommandCorrectionExcludedCommand, isWindowOrDiffPageTransitionCommand, } from "../../command-taxonomy.js";
|
|
11
11
|
import { chooseOpenResultTabCorrection } from "../../runtime.js";
|
|
12
12
|
import { isRecord, parseRefId } from "../../parsing.js";
|
|
13
13
|
import { getUpstreamEffectiveBatchSteps } from "../batch-stdin.js";
|
|
@@ -424,7 +424,7 @@ export function commandChoosesSessionTabTarget(args) {
|
|
|
424
424
|
|| isWindowOrDiffPageTransitionCommand(command, subcommand)
|
|
425
425
|
|| (command === "a11y" && findFirstPositionalArgument(tokens) !== undefined)
|
|
426
426
|
|| (["vitals", "web-vitals"].includes(command) && tokens.slice(1).some((token) => !token.startsWith("--")))
|
|
427
|
-
|| (
|
|
427
|
+
|| getRecordCommandOperands(tokens).url !== undefined;
|
|
428
428
|
}
|
|
429
429
|
export function shouldPinSessionTabForCommand(options) {
|
|
430
430
|
if (!options.pinningRequired || !options.sessionName || !options.command)
|
|
@@ -15,7 +15,7 @@ export const QUICK_START_GUIDELINES = [
|
|
|
15
15
|
`Common advanced calls: { args: ["batch", "--bail"], stdin: "[[\"open\",\"https://example.com\"],[\"snapshot\",\"-i\"]]" }, { job: { steps: [{ action: "open", url: "https://example.com" }, { action: "assertText", text: "Example Domain" }, { action: "screenshot", path: ".dogfood/example.png" }] } }, { qa: { url: "https://example.com", expectedText: "Example Domain", screenshotPath: ".dogfood/qa-example.png" } } (example.com smoke only; elsewhere match exact visible text from snapshot -i), { electron: { action: "list", query: "code" } }, { electron: { action: "launch", appName: "Visual Studio Code", handoff: "snapshot" } }, { electron: { action: "probe" } }, { qa: { attached: true, expectedText: "Explorer" } }, { args: ["eval", "--stdin"], stdin: "document.title", outputPath: "logs/page-title.json" }, { args: ["auth", "save", "name", "--password-stdin"], stdin: "<password from user-approved secret source>" }, { args: ["--profile", "Default", "open", "https://example.com/account"], sessionMode: "fresh" }, and { args: ["open", "--enable", "react-devtools", "https://example.com"], sessionMode: "fresh" }. For app pages with a native dropdown, job steps can include { action: "select", selector: "#flavor", value: "chocolate" } before the dependent assertion; for locator-friendly pages, job click/fill steps can use semantic locator fields such as { action: "fill", locator: "role", role: "searchbox", name: "Search", text: "agent browser" }; for human-paced input, job type steps can use { action: "type", selector: "#prompt", text: "hello", delayMs: 20, press: "Enter" }; delayed typing is capped at 200 characters per step, and generated per-character rows are compacted in visible batch prose while full rows remain in details.batchSteps.`,
|
|
16
16
|
"Constrained job navigation is explicit only: click (and select/submit flows that may navigate) does not prove the next page loaded; add an assertUrl that does not already match the starting page and/or assertText for new page state after navigation-prone steps before screenshot or later interactions. assertText takes only text, not selector or locator fields. Clicks can stale subsequent @refs: split the job and re-snapshot before using those refs. Keep jobs short around navigation, click, and rerender boundaries on dynamic React/product apps; avoid a whole checkout in one job. If a long job times out and details.timeoutPartialProgress shows a mutating incomplete step, inspect current page state and continue with a shorter job or single action instead of blindly retrying the mutating step. Example: { job: { steps: [{ action: \"open\", url: \"https://shop.example/checkout\" }, { action: \"fill\", selector: \"#email\", text: \"user@example.com\" }, { action: \"click\", selector: \"#continue\" }, { action: \"assertUrl\", url: \"**/shipping\" }, { action: \"assertText\", text: \"Shipping address\" }, { action: \"screenshot\", path: \".dogfood/shipping.png\" }] } }. Top-level click may add pageChangeSummary hints, but job never auto-inserts post-click asserts.",
|
|
17
17
|
"High-value command reference: click <selector> --new-tab opens link-like targets in a new tab; select <selector> <value...> changes native dropdown values; wrapper-handled scroll <dir> [px|percent] and scroll to end/top target document scrolling before upstream fallback, while scroll <selector> <dir> [px|percent] targets nested scrollers; download <selector> <path> saves a file triggered by a click; read [url] returns agent-readable text (explicit URLs prefer markdown without requiring a Chrome page; omit the URL for rendered active-tab DOM); get title/url need no selector; get text/html/value/count <selector> and get attr <selector> <name> read elements/page state (use body for whole-page text/html); screenshot [selector] [path] captures a page or element image; pdf <path> saves a PDF; tab list and tab <tab-id-or-label> inspect or recover the active tab; react tree, react inspect <fiberId>, react renders start/stop, and react suspense introspect React after --enable react-devtools; vitals [url] measures Core Web Vitals; pushstate <url> performs SPA navigation; tap <selector> and swipe <direction> [distance] support iOS/provider touch flows.",
|
|
18
|
-
"For artifact-producing commands, read the visible artifact block and details.artifactVerification before using files: check requested path, absolute path, existence, size bytes, artifact kind, optional mediaType, status, optional limitation, and verified/missing/pending/unverified counts. details.artifacts contains per-file metadata; record start rows are pending/openRecording until record stop writes the target.
|
|
18
|
+
"For artifact-producing commands, read the visible artifact block and details.artifactVerification before using files: check requested path, absolute path, existence, size bytes, artifact kind, optional mediaType, status, optional limitation, and verified/missing/pending/unverified counts. details.artifacts contains per-file metadata; record start rows are pending/openRecording until record stop writes the target. Current upstream records the active page unless a URL is supplied. To support older natives, the wrapper conservatively blocks prior @e… refs after every dispatched start attempt and URL-bearing restart, even on failure; this does not prove the page changed. Take a fresh snapshot before continuing. A restart with only --fps keeps the page and refs; --fps <n> selects 1–60 fps (default 30). The wrapper creates parent directories for direct artifact paths and can save simple loopback HTTP(S) anchor downloads directly to the requested path before upstream download fallback. Browser close does not delete explicit saved files; if close reports details.artifactCleanup, use host file tools to remove paths listed in explicitArtifactPaths (when non-empty) after inspection. If close fails with details.promptGuard.reason=requested-artifacts-missing-before-close, save the exact required artifact path before closing. A bare inbound image/video path is not a requested output artifact and does not block close. For annotated screenshots inside batch, put --annotate in top-level args (for example { args: [\"--annotate\", \"batch\"], stdin: \"[[\\\"screenshot\\\",\\\"/tmp/page.png\\\"]]\" }) rather than inside the screenshot step; if annotation labels crowd a dense page, use a scoped or non-annotated screenshot plus snapshot refs instead.",
|
|
19
19
|
"When failure output shows Next actions, prefer those exact native agent_browser follow-up payloads over guessed commands. The same actions are available in details.nextActions to callers that expose structured details; short stdin is shown inline, while long stdin stays details-only.",
|
|
20
20
|
];
|
|
21
21
|
export const WEB_SEARCH_PROMPT_GUIDELINE = "Prefer agent_browser_web_search for current or external web facts and URL discovery over public search-engine forms that can hit anti-bot/CAPTCHA-gated pages. For research before implementation, pass searchType: deep-lite unless webSearch.defaultSearchType already does; omit it for everyday lookups so config/auto wins. Provider rank is not proof of authority: when correctness or version matters, prefer the vendor or project's primary current docs, inspect page-date and version clues, and constrain one follow-up after discovering the official domain (Exa includeDomains; Brave site: in query). Do not count URL aliases as independent sources. Use agent_browser after you have a target URL that needs interaction, screenshots, or DOM inspection.";
|
|
@@ -35,7 +35,7 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
35
35
|
"After a successful `connect`, `--cdp`, or enabled `--auto-connect` call, verify with get url and keep using the resulting session without repeating the attach flag. The wrapper remembers that attachment across active-branch reload/resume and live-checks the URL before later page reads/interactions because an attached browser can drift externally; caller config, file access, launch arguments, and environment pass through unchanged. A successful close clears the marker. When several named sessions share one Chrome, pass --pin-tab once (AGENT_BROWSER_PIN_TAB) so a closed bound tab fails as tab_gone instead of acting on a neighbor; recover with tab new or tab list. --no-pin-tab turns the sticky pin off. tab list includes each tab's CDP targetId, accepted as a tab ref.",
|
|
36
36
|
`If you already used the implicit session and now need launch-scoped flags (${LAUNCH_SCOPED_FLAG_LABEL}), retry with top-level sessionMode set to fresh or pass an explicit --session for the new launch; never pass --session-mode inside args. After a successful unnamed fresh launch, later auto calls follow that new session.`,
|
|
37
37
|
"For WebGPU pages, use args [\"--webgpu\", \"open\", \"<url>\"] on a fresh local browser launch; use doctor --webgpu (or --headed on Linux/Windows capture paths) to prove rendering before trusting a non-black screenshot. WebGPU cannot be combined with --cdp, --auto-connect, or provider launches unless --webgpu false overrides an enabled config/environment default.",
|
|
38
|
-
"For experimental WebMCP page tools, use webmcp list, then webmcp invoke <tool> with --params and optional --frame/--detach/--timeout; use webmcp result or cancel for detached calls. Locally managed Chrome enables WebMCP by default. --no-webmcp is launch-scoped and requires a fresh session; invoke/result/cancel can mutate or navigate, so refresh snapshot refs afterward.",
|
|
38
|
+
"For experimental WebMCP page tools, use webmcp list, then webmcp invoke <tool> with --params and optional --frame/--detach/--timeout; use webmcp result or cancel for detached calls. Locally managed Chrome enables WebMCP by default; a positive navigation hint means the page has tools to list. --no-webmcp is launch-scoped and requires a fresh session; invoke/result/cancel can mutate or navigate, so refresh snapshot refs afterward.",
|
|
39
39
|
"For --allowed-domains, use a fresh local Chrome context. Upstream rejects CDP/auto-connect, profiles, restore/state replay, direct-page providers, iOS/Safari, and startup/profile Chrome args because they cannot guarantee containment; Chromium also disables RTCPeerConnection while the allowlist is active.",
|
|
40
40
|
"For React introspection, launch the page with --enable react-devtools before first navigation, then use react tree, react inspect <fiberId>, sourceLookup candidates for local UI source hints, react renders start/stop, or react suspense; sourceLookup is experimental and reports confidence/evidence instead of guaranteed DOM-to-file mappings. For failed fetches and APIs, networkSourceLookup (experimental) correlates failed network requests with initiator metadata and bounded workspace URL literals—candidates only, not definitive blame. Use vitals [url] for Core Web Vitals and hydration timing, and pushstate <url> for client-side SPA navigation.",
|
|
41
41
|
"For first-navigation setup, use open without a URL plus network route --resource-type <csv>, cookies set --curl <file>, or --init-script/--enable before navigate/opening the target page.",
|
|
@@ -57,7 +57,7 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
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.",
|
|
60
|
-
"
|
|
60
|
+
"Recording needs ffmpeg on PATH before start. Current upstream checks it at startup; older natives may defer failure. A pending recording is not verified output.",
|
|
61
61
|
"Do not call --help or other exploratory inspection commands unless the user explicitly asks for them or debugging the browser integration is necessary.",
|
|
62
62
|
];
|
|
63
63
|
export const TOOL_PROMPT_GUIDELINES_SUFFIX = [
|
|
@@ -80,7 +80,7 @@ export const RUNTIME_PROMPT_GUIDELINES = [
|
|
|
80
80
|
"Use agent_browser with one input mode: script, args, semanticAction, job, qa, sourceLookup/networkSourceLookup, or electron. stdin: batch/eval/auth/wrapper batch only; electron rejects it; never pass --json.",
|
|
81
81
|
"For agent_browser, use open → snapshot -i → @refs; re-snapshot after changes. In authenticated unattended/auto-approved employee flows, ordinary requested non-destructive submissions may proceed. Honor explicit stops; require explicit authorization for purchases, production-control, destructive/irreversible, or account/security/privacy changes.",
|
|
82
82
|
"Use agent_browser sessionMode=fresh for launch flags. Use requested/configured profiles only; run profiles/doctor on failure. --allowed-domains cannot restore; macOS profile copies may omit encrypted cookies. Verify auth; use a user-approved headed login if needed. Profile content is model-visible.",
|
|
83
|
-
"agent_browser: exact user paths; verify artifactVerification/artifacts before success claims. Save promptGuard-required files before close;
|
|
83
|
+
"agent_browser: exact user paths; verify artifactVerification/artifacts before success claims. Save promptGuard-required files before close; ffmpeg before recording; close keeps files; waited:timeout proves nothing.",
|
|
84
84
|
"When agent_browser details.nextActions exists, use them. Check Omitted high-value controls in dense snapshots. Dashboards: verify scroll via screenshot/snapshot.",
|
|
85
85
|
"agent_browser: read <url> for docs/text or active DOM; get title/url; get text/html/value/count <selector>; get attr <selector> <name>. Batch 3+ getters; heed visibility warnings.",
|
|
86
86
|
];
|
|
@@ -3,7 +3,6 @@ import { extname, resolve } from "node:path";
|
|
|
3
3
|
import { getAgentBrowserSessionIdentityKey } from "../../argv-grammar.js";
|
|
4
4
|
import { getExplicitArtifactDestination } from "../../orchestration/browser-run/artifact-paths.js";
|
|
5
5
|
import { isRecord, parsePositiveInteger } from "../../parsing.js";
|
|
6
|
-
import { extractUpstreamCommandTokens } from "../../runtime.js";
|
|
7
6
|
import { formatSessionArtifactRetentionSummary, getSessionArtifactManifestEntryKey, isPendingRecordingArtifact, isPendingRecordingCommand, mergeSessionArtifactManifest, } from "../artifact-manifest.js";
|
|
8
7
|
import { classifyAgentBrowserSuccessCategory } from "../categories.js";
|
|
9
8
|
const PNG_HEADER = Buffer.from("89504e470d0a1a0a0000000d49484452", "hex");
|
|
@@ -192,7 +191,7 @@ async function buildFileArtifactMetadata(options) {
|
|
|
192
191
|
namespace: options.namespace,
|
|
193
192
|
path: displayPath,
|
|
194
193
|
recordingState: pendingRecording ? "openRecording" : undefined,
|
|
195
|
-
requestedPath: options.artifactRequest?.path ?? getExplicitArtifactDestination(
|
|
194
|
+
requestedPath: options.artifactRequest?.path ?? getExplicitArtifactDestination(options.commandInfo.commandTokens ?? []),
|
|
196
195
|
session: options.sessionName,
|
|
197
196
|
sizeBytes,
|
|
198
197
|
status: pendingRecording ? "pending" : exists === false ? "missing" : stale ? "stale" : options.artifactRequest?.status ?? "saved",
|
|
@@ -213,44 +212,33 @@ async function buildPreviousRestartRecordingArtifact(options) {
|
|
|
213
212
|
if (!previousRecording)
|
|
214
213
|
return undefined;
|
|
215
214
|
const absolutePath = previousRecording.absolutePath ?? resolve(options.cwd, previousRecording.path);
|
|
215
|
+
const base = {
|
|
216
|
+
absolutePath,
|
|
217
|
+
artifactType: "video",
|
|
218
|
+
command: "record",
|
|
219
|
+
cwd: previousRecording.cwd ?? options.cwd,
|
|
220
|
+
extension: previousRecording.extension ?? (extname(absolutePath).toLowerCase() || undefined),
|
|
221
|
+
kind: "video",
|
|
222
|
+
namespace: previousRecording.namespace ?? options.namespace,
|
|
223
|
+
path: previousRecording.path,
|
|
224
|
+
requestedPath: previousRecording.requestedPath,
|
|
225
|
+
session: previousRecording.session ?? options.sessionName,
|
|
226
|
+
subcommand: "restart-previous",
|
|
227
|
+
};
|
|
216
228
|
try {
|
|
217
229
|
const fileStats = await stat(absolutePath);
|
|
218
230
|
const stale = artifactMtimeIsOutsideCommandWindow(fileStats.mtimeMs, options.artifactMinUpdatedAtMs, options.artifactMaxUpdatedAtMs);
|
|
219
231
|
return {
|
|
220
|
-
|
|
221
|
-
artifactType: "video",
|
|
222
|
-
command: "record",
|
|
223
|
-
cwd: previousRecording.cwd ?? options.cwd,
|
|
232
|
+
...base,
|
|
224
233
|
exists: true,
|
|
225
|
-
extension: previousRecording.extension ?? (extname(absolutePath).toLowerCase() || undefined),
|
|
226
|
-
kind: "video",
|
|
227
234
|
mediaType: fileStats.isFile() ? await getFileImageMimeType(absolutePath) : undefined,
|
|
228
|
-
namespace: previousRecording.namespace ?? options.namespace,
|
|
229
|
-
path: previousRecording.path,
|
|
230
|
-
requestedPath: previousRecording.requestedPath,
|
|
231
|
-
session: previousRecording.session ?? options.sessionName,
|
|
232
235
|
sizeBytes: fileStats.size,
|
|
233
236
|
status: stale ? "stale" : "saved",
|
|
234
|
-
subcommand: "restart-previous",
|
|
235
237
|
updatedAtMs: fileStats.mtimeMs,
|
|
236
238
|
};
|
|
237
239
|
}
|
|
238
240
|
catch {
|
|
239
|
-
return {
|
|
240
|
-
absolutePath,
|
|
241
|
-
artifactType: "video",
|
|
242
|
-
command: "record",
|
|
243
|
-
cwd: previousRecording.cwd ?? options.cwd,
|
|
244
|
-
exists: false,
|
|
245
|
-
extension: previousRecording.extension ?? (extname(absolutePath).toLowerCase() || undefined),
|
|
246
|
-
kind: "video",
|
|
247
|
-
namespace: previousRecording.namespace ?? options.namespace,
|
|
248
|
-
path: previousRecording.path,
|
|
249
|
-
requestedPath: previousRecording.requestedPath,
|
|
250
|
-
session: previousRecording.session ?? options.sessionName,
|
|
251
|
-
status: "missing",
|
|
252
|
-
subcommand: "restart-previous",
|
|
253
|
-
};
|
|
241
|
+
return { ...base, exists: false, status: "missing" };
|
|
254
242
|
}
|
|
255
243
|
}
|
|
256
244
|
export async function extractFileArtifacts(options) {
|
|
@@ -436,7 +424,7 @@ function formatArtifactLabel(artifact) {
|
|
|
436
424
|
}
|
|
437
425
|
if (!isPendingRecordingArtifact(artifact))
|
|
438
426
|
return "Saved recording";
|
|
439
|
-
return artifact.subcommand === "restart" ? "Recording restarted; output will be written on stop" : "Recording started
|
|
427
|
+
return artifact.subcommand === "restart" ? "Recording restarted; output will be written on stop" : "Recording started; output will be written on stop";
|
|
440
428
|
}
|
|
441
429
|
}
|
|
442
430
|
export function formatArtifactSummary(artifacts) {
|
|
@@ -64,11 +64,11 @@ export function getPageSummary(data) {
|
|
|
64
64
|
const url = typeof data.url === "string" ? data.url : undefined;
|
|
65
65
|
if (title === undefined && url === undefined)
|
|
66
66
|
return undefined;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
const summary = title && url ? `${title}\n${url}` : url || title || UNTITLED_PAGE_SUMMARY;
|
|
68
|
+
const webmcp = isRecord(data.webmcp) ? data.webmcp : undefined;
|
|
69
|
+
return webmcp?.available === true && typeof webmcp.toolCount === "number" && Number.isInteger(webmcp.toolCount) && webmcp.toolCount > 0
|
|
70
|
+
? `${summary}\n\nWebMCP tools are available on this page (experimental). Run webmcp list to view them.`
|
|
71
|
+
: summary;
|
|
72
72
|
}
|
|
73
73
|
export function formatCount(count, singular, plural = `${singular}s`) {
|
|
74
74
|
return `${count} ${count === 1 ? singular : plural}`;
|
|
@@ -212,7 +212,7 @@ export function buildNoActivePageRefSnapshotInvalidation() {
|
|
|
212
212
|
export function buildPageTransitionRefSnapshotInvalidation(summary) {
|
|
213
213
|
return {
|
|
214
214
|
reason: "page-transition",
|
|
215
|
-
summary: summary ?? "
|
|
215
|
+
summary: summary ?? "Recording starts and URL-bearing restarts conservatively invalidate earlier page-scoped refs. Run snapshot -i before using refs; this is not evidence of a page change.",
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
218
|
export function getCommandRefSnapshotInvalidation(commandTokens) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const TARGET_AGENT_BROWSER_SOURCE = "scripts/agent-browser-target.mjs";
|
|
2
|
-
export const TARGET_AGENT_BROWSER_VERSION = "0.
|
|
2
|
+
export const TARGET_AGENT_BROWSER_VERSION = "0.37.0";
|
|
3
3
|
export const TARGET_AGENT_BROWSER_VERSION_LABEL = `agent-browser ${TARGET_AGENT_BROWSER_VERSION}`;
|
|
4
4
|
export const MINIMUM_AGENT_BROWSER_VERSION = "0.35.0";
|
|
5
5
|
export const MINIMUM_AGENT_BROWSER_VERSION_LABEL = `agent-browser ${MINIMUM_AGENT_BROWSER_VERSION}`;
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -165,7 +165,7 @@ Practical policy:
|
|
|
165
165
|
- redact snapshot spill payloads before writing them, clean up process-private temp spill artifacts on shutdown, and keep persisted-session spill files in a private session-scoped artifact directory with a bounded per-session budget so `details.fullOutputPath` stays usable after reload/resume without unbounded growth
|
|
166
166
|
- keep explicit screenshots, downloads, PDFs, traces, HAR captures, and recordings written to caller-chosen paths on disk after a successful upstream close command (`close`, `quit`, or `exit`); before artifact-producing commands run, create missing parent directories for requested host paths, and for simple loopback HTML anchor downloads with resolvable HTTP(S) hrefs the wrapper may save directly to the requested path before upstream fallback. When the bounded `details.artifactManifest` has entries, successful close commands also surface `details.artifactCleanup` and a compact `Artifact lifecycle` note pointing to structured explicit paths so operators remove files with normal host tools—the native tool does not delete arbitrary user paths (`extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts`, `getArtifactCleanupGuidance`); contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), checklist `RQ-0079` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
|
|
167
167
|
- reconstruct the current branch-visible extension-managed session, every transcript-proven still-active wrapper-owned managed identity, page-scoped refs, newest-revision aggregate artifact manifest, unbounded active-recording reservation events, and Electron launch records from the active transcript branch on `session_start` and `session_tree` so later default and explicit off-current calls keep following owned browsers after resume/reload or branch switching; restore also honors successful explicit `--session <wrapper-owned> close` rows, terminal nested-batch close outcomes even when aggregate artifact verification failed, and `electron.cleanup` managed-session steps so closed wrapper-owned sessions are not resurrected; a nested close invalidates the pre-close page target so a lifecycle-proven relaunch at `about:blank` is not treated as stale focus drift; explicit lifecycle evidence that a later diagnostic did not launch a browser preserves the terminal close, while any later row—including a failed row—whose lifecycle reports a browser launch keeps active/attached provenance; failed-step presentation persists only that bounded launch boolean so transcript replay reaches the same decision, missing lifecycle evidence remains conservatively active even on the first managed call, successful closes clear wrapper trace/profiler ownership before ordered later successful rows can rebuild it, namespace-scoped `close --all` clears all matching managed/attached/page/ref/route/trace/recording ownership, and recording starts after close are rejected before spawn
|
|
168
|
-
- keep active recording destination reservations separate from the bounded metadata-only artifact manifest. The process-wide map is keyed by canonical namespace/session identity, rebuilt from append-only branch events, and retained for still-live process-owned recordings across branch switches. Shutdown/reload appends both terminal tombstones and still-live reservations onto the current branch so restart cannot resurrect a cross-branch close or lose a live-daemon reservation. One artifact lifecycle/output queue makes global destination preflight and reservation updates atomic across otherwise-concurrent caller-owned session queues. Every successful direct, ordered nested-batch, managed replacement, script, Electron, or shutdown close retires its exact identity at that lifecycle point; only the newest pending recording path remains authoritative across current transition replay (including same-timestamp restart rows), and recording starts after a nested close are rejected because upstream can falsely report success. Existing and dangling symlink ancestry, hardlink inode identity, full Unicode/platform case folding, and same-call `outputPath` comparison prevent alias reuse. One shared command-token projection mirrors upstream's full-argv global cleanup before artifact, recording, and presentation parsing; wait-download detection removes only the first timeout pair, follows upstream long/short mode precedence, and accepts both `--download` and `-d` wherever download mode wins; screenshot destinations use upstream's exact-flag, selector-prefix, case-sensitive extension, slash-path, and second-positional rules, while retaining the wrapper's intentional slash-bearing hidden-workspace path normalization. Current recording transitions are replayed directly; artifact manifests are not treated as reservation events
|
|
168
|
+
- keep active recording destination reservations separate from the bounded metadata-only artifact manifest. The process-wide map is keyed by canonical namespace/session identity, rebuilt from append-only branch events, and retained for still-live process-owned recordings across branch switches. Shutdown/reload appends both terminal tombstones and still-live reservations onto the current branch so restart cannot resurrect a cross-branch close or lose a live-daemon reservation. One artifact lifecycle/output queue makes global destination preflight and reservation updates atomic across otherwise-concurrent caller-owned session queues. Every successful direct, ordered nested-batch, managed replacement, script, Electron, or shutdown close retires its exact identity at that lifecycle point; only the newest pending recording path remains authoritative across current transition replay (including same-timestamp restart rows), and recording starts after a nested close are rejected because upstream can falsely report success. Existing and dangling symlink ancestry, hardlink inode identity, full Unicode/platform case folding, and same-call `outputPath` comparison prevent alias reuse. One shared command-token projection mirrors upstream's full-argv global cleanup before artifact, recording, and presentation parsing; wait-download detection removes only the first timeout pair, follows upstream long/short mode precedence, and accepts both `--download` and `-d` wherever download mode wins; screenshot destinations use upstream's exact-flag, selector-prefix, case-sensitive extension, slash-path, and second-positional rules, while retaining the wrapper's intentional slash-bearing hidden-workspace path normalization. Recording path/URL consumers share a command-local reader that skips complete numeric `--fps` pairs without rewriting argv or treating them as outer globals; native owns rate, format and extra-argument validation. Current recording transitions are replayed directly; artifact manifests are not treated as reservation events
|
|
169
169
|
- keep process-owned cleanup registries for extension-managed sessions and wrapper-launched Electron records separate from the current branch-visible view; `session_tree` restore and wrapper-owned browser commands are serialized with managed-session work, while caller-owned explicit-session commands are serialized by process-local queues keyed to effective canonical namespace/session across prepare helpers (explicit namespace argv overrides inherited `AGENT_BROWSER_NAMESPACE`, including an explicit empty default) and main execution. macOS and Windows additionally normalize and case-fold namespace and session components to match case-insensitive daemon identity. Different caller-owned identities remain concurrent, except namespace-scoped `close --all` drains and exclusively barriers managed plus matching caller-owned work before clearing global namespace state; nested helpers never re-enter the outer queue, policy/route/artifact deltas merge across unrelated managed-state commits, and a separate branch-restore generation guard prevents stale completions from overwriting newer branch-visible state; aggregate artifact results use monotonic revisions so transcript replay cannot lose a concurrently completed entry. Branch switches still must not drop resources the current Pi process owns and must keep fresh-session allocation monotonic
|
|
170
170
|
- record successful `connect`, `--cdp`, enabled `--auto-connect`, environment-configured CDP/auto-connect, and wrapper Electron attachment identities in branch-visible state. First-use and later content-bearing calls live-check `get url` because attached targets can drift outside Pi. Caller config, file access, launch arguments, and environment pass through unchanged; only wrapper-injected compatibility launch arguments are omitted on active attachments. A terminal successful close removes the marker; a close followed by a later step whose lifecycle reports a browser launch preserves it, while a non-launching diagnostic such as `stream status` leaves the close terminal
|
|
171
171
|
- when a successful close targets the current extension-managed session, including an explicit `--session <current> close` or an `electron.cleanup` managed-session step, clear page/ref state, mark that session inactive, untrack cleanup ownership, and rotate the next default auto call to a fresh wrapper-generated session name rather than reusing the closed name
|
|
@@ -178,7 +178,8 @@ Practical policy:
|
|
|
178
178
|
- once the wrapper observes tab-drift risk for a session (profile restore correction, overlapping stale opens, or restored session state), later active-tab commands verify the intended tab under the existing session queue before semantic/ref helpers and user commands. Native selection runs only when the intended tab is not already active, because upstream selection clears refs and frame scope even on same-tab reselection. Missing targets, failed selection, and post-selection target mismatches fail before user commands. Caller argv/stdin and native `--pin-tab` / `--no-pin-tab` preferences remain unchanged. Local commands, live `get url`, explicit HTTP `read <url>` (including its flags), URL `a11y`/`vitals`/`web-vitals`, `diff url`, `window new`, URL-bearing recording commands, and explicit tab/navigation/`connect`/`state load` recovery do not require the old target; history back/forward/reload, `pushstate`, and page-content operations still do. The same classifier scans effective batch rows past non-page prefixes until a page dependency or explicit context change, without rewriting user rows or changing bail behavior. For `window new` and `diff url`, observe the resulting URL instead of retaining the old target or treating the requested second URL as redirect evidence. Fold only reached native batch rows when available, discard observations and refs from before those transitions, and let later successful snapshots rebuild refs even when another batch row fails. Retain an observed blank destination after either command instead of recovering the old page; if no final target is observed, use the existing unknown-target state. Caller batch arguments, stdin and bail behavior stay unchanged. Routine same-session commands avoid `tab list` preflights
|
|
179
179
|
- for sessions with observed tab-drift risk, after a successful command on a known tab target, the wrapper may best-effort restore that same target again if restored/background tabs steal focus after the command returns; routine same-session commands skip this post-command `tab list` probe
|
|
180
180
|
- after successful standalone tab selection or `tab close`, read the now-active URL and fresh non-blank title—even when two tabs share a URL—before updating per-session page state because upstream selection/close payloads are not sufficient page-target evidence; retain an explicitly selected existing `about:blank` tab or a blank tab revealed by close instead of treating either as accidental drift
|
|
181
|
-
- keep a per-session `refSnapshot` aligned with the last successful `snapshot` (including refs merged from a successful `batch` by taking the last successful `snapshot` step in batch result order): restore it from persisted tool `details` when reloading, resuming, or moving to a different Pi session-tree branch, store bounded ref role/name metadata from the same snapshot for wrapper-side current-ref diagnostics, drop it on successful close commands (`close`, `quit`, or `exit`), replace it with a persisted `page-transition` invalidation after any upstream-executed `record start` attempt (direct or batch;
|
|
181
|
+
- keep a per-session `refSnapshot` aligned with the last successful `snapshot` (including refs merged from a successful `batch` by taking the last successful `snapshot` step in batch result order): restore it from persisted tool `details` when reloading, resuming, or moving to a different Pi session-tree branch, store bounded ref role/name metadata from the same snapshot for wrapper-side current-ref diagnostics, drop it on successful close commands (`close`, `quit`, or `exit`), replace it with a persisted `page-transition` invalidation after any upstream-executed `record start` attempt (direct or batch; older supported natives can swap the page before their already-active check, so failed starts count; the warning and persisted summary explicitly describe conservative ref invalidation, not an observed page change), a `record restart` with a URL operand, `window new`, `diff url`, or WebMCP `invoke` / `result` / `cancel` (these page-provided tools can mutate, rerender, or navigate; when a spawned `batch` yields no parseable result rows, for example after a wrapper timeout, planned transition steps still record the invalidation), or after a failed non-batch transition command (`eval`, `back`, `forward`, `reload`, `connect`, `state load`, `tab` selection) whose live URL re-verification probe observed the page (a failed transition can still have mutated or replaced the document before throwing, so keeping the verified URL must not keep the prior refs; transcript replay preserves the persisted invalidation summary), and refuse page-scoped `@e…` argv before spawn when the active tab URL no longer matches the snapshot URL, when a ref id was never in that snapshot, when the snapshot state is invalidated, or when a `batch` step would reuse `@e…` on a guarded getter or mutation step after an earlier invalidating step (including `record start`, URL-bearing `record restart`, and WebMCP `invoke` / `result` / `cancel`) without a later `snapshot` step in the same plan; batch steps come from the source upstream actually executes (raw batch argument strings exclusively when any exist — filtering only the exact `--bail` token like upstream — stdin only otherwise, via `getUpstreamEffectiveBatchSteps` in `extensions/agent-browser/lib/orchestration/batch-stdin.ts`); tab recovery (which leaves user argv/stdin and continue-on-error control flow unchanged), artifact/recording preflight, batch screenshot path preparation (parent directories are created for effective raw rows too, without rewriting raw strings), and stale-ref echo args use that same selection so pinning and preflights cannot act on upstream-ignored stdin, while the pre-spawn state-policy validator deliberately keeps scanning parseable stdin alongside argv as a fail-closed content superset and treats stdin parse failures as fatal only when upstream would actually read stdin (its raw-token filter also uses the exact `--bail` token only). Same-snapshot `fill @e…` rows are guarded but do not themselves set that invalidation latch, so ordinary form fills can precede a click/submit row in one batch—see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) for the agent-visible contract and failure text; typed per-session tab/ref/pinning state lives in `extensions/agent-browser/lib/session-page-state.ts` and is updated from `extensions/agent-browser/index.ts` after each tool result
|
|
182
|
+
- native 0.37 owns first-document setup inheritance for new tabs; the wrapper does not copy headers, user agents or other browser setup itself. The page-summary formatter displays native positive WebMCP availability while retaining raw details; it adds no discovery probe
|
|
182
183
|
- when a direct or batched WebMCP call returns `status: "pending"`, or `result` / `cancel` fails while that target is unknown, keep its tab target unknown and discard same-call snapshot evidence instead of treating an immediate post-dispatch URL probe as stable; `webmcp result` / `cancel`, `get url`, and explicit navigation remain available while unknown; replace the generic blocked snapshot action with `verify-page-target-after-pending-webmcp` (`get url`), and let a completed `batch --bail` use that verification before `snapshot -i` to re-establish both target and refs
|
|
183
184
|
- for top-level non-Electron direct `click` commands with an eligible target, install a bounded in-page target-specific event probe before upstream runs; if upstream reports success but no trusted pointer/mouse/click event reached the resolved target, fail the tool and report `details.clickDispatch` with explicit retry/inspect next actions (the wrapper does not replay clicks in-page). The probe covers `xpath=` targets and current `@e…` / `ref=` refs whose latest stored `refSnapshot.refs` role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`; it requires a unique role/name in the saved snapshot and the live candidates instead of taking a fresh pre-click snapshot that could recycle upstream refs. Duplicate-name refs pass through without a probe: their old ordinal is not target identity. The probe is intentionally skipped for CSS selector clicks, unresolved `find … click` locators, and `batch`/`job`/`qa` click steps
|
|
184
185
|
- derive narrow prompt guards only for concrete evidence invariants: explicitly requested screenshot/recording output paths block browser close until the artifact manifest verifies those paths, while bare inbound attachment paths remain inputs. The wrapper intentionally does not infer broad business/user intent from prompt text such as order/payment/post boundaries; agents must follow those instructions themselves. The artifact guard is bounded preflight policy (`details.promptGuard`, `failureCategory: "policy-blocked"`), not a reusable browser recipe layer
|
|
@@ -196,7 +197,7 @@ The extension should surface that clearly and avoid hidden restart behavior in v
|
|
|
196
197
|
|
|
197
198
|
That means explicit startup-scoping flags like `--allowed-domains`, `--auto-connect`, `--args`, `--user-agent`, `--cdp`, `--enable`, `--executable-path`, `--webgpu`, `--no-webmcp`, `--headed`, `--init-script`, `--device`, `--namespace`, `--profile`, `--provider`, `-p`, `--restore`, `--restore-save`, restore check flags, `--session-name`, and `--state` should remain explicit upstream argv choices instead of being wrapped in extra hidden restart or cloning logic. The one deliberate exception is the env-only managed-session `AGENT_BROWSER_RESTORE` key above, which does not inject `--restore` into argv and therefore does not trip launch-scoped `sessionMode: "fresh"` recovery.
|
|
198
199
|
|
|
199
|
-
The wrapper may still apply narrow compatibility normalizations when observed behavior justifies them and the result remains thin, local, and opt-out. For example, OpenAI web properties and `dash.cloudflare.com` reject the default local `HeadlessChrome` user agent while the same flow works with a normal Chrome UA, so the extension injects a domain-specific fallback only when the caller did not already choose raw Chrome arguments, a custom user agent, headed mode, CDP, auto-connect, a provider-backed launch, or a non-Chrome engine through argv or matching upstream environment. Managed sessions retain the injected value as per-session wrapper state across helper calls and branch reload/resume. Active daemons omit both launch forms so upstream does not replace a launch-configured browser; a session proven inactive receives the retained compatibility launch values, including the same fixed compatibility value as a comma-safe Chrome launch argument covering tabs and SSO popups that do not inherit
|
|
200
|
+
The wrapper may still apply narrow compatibility normalizations when observed behavior justifies them and the result remains thin, local, and opt-out. For example, OpenAI web properties and `dash.cloudflare.com` reject the default local `HeadlessChrome` user agent while the same flow works with a normal Chrome UA, so the extension injects a domain-specific fallback only when the caller did not already choose raw Chrome arguments, a custom user agent, headed mode, CDP, auto-connect, a provider-backed launch, or a non-Chrome engine through argv or matching upstream environment. Managed sessions retain the injected value as per-session wrapper state across helper calls and branch reload/resume. Active daemons omit both launch forms so upstream does not replace a launch-configured browser; a session proven inactive receives the retained compatibility launch values, including the same fixed compatibility value as a comma-safe Chrome launch argument covering tabs and SSO popups on supported native versions that do not inherit per-page CDP overrides. Wrapper-owned headed launches also default upstream periodic restore autosave off because agent-browser 0.33.2 collects non-current origins through visible temporary targets while holding the daemon state lock; save-on-close remains enabled, and an explicit `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` value opts in when the daemon launches. The effective interval is retained in owned-session state and transcript results; changing it in either direction on a running wrapper-owned headed daemon is rejected until close plus a fresh launch.
|
|
200
201
|
|
|
201
202
|
If the current managed session is already active and one of those startup-scoped flags appears again while `sessionMode` is still `"auto"`, the extension should fail clearly instead of silently sending a command shape that upstream would ignore. An explicitly targeted older wrapper-owned session gets the same protection after daemon inspection proves it active.
|
|
202
203
|
|