pi-agent-browser-native 0.2.76 → 0.3.0
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 +33 -0
- package/README.md +10 -19
- package/dist/extensions/agent-browser/index.js +221 -23
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +0 -5
- package/dist/extensions/agent-browser/lib/argv-grammar.js +4 -5
- package/dist/extensions/agent-browser/lib/command-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +0 -6
- package/dist/extensions/agent-browser/lib/config-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/config.js +0 -6
- package/dist/extensions/agent-browser/lib/electron/cdp.js +6 -7
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/discovery.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/launch.js +0 -7
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/job.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -6
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/types.js +0 -5
- package/dist/extensions/agent-browser/lib/json-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +4 -5
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +0 -3
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +30 -13
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +0 -5
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +1 -7
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +0 -5
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +41 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +20 -11
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +42 -20
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +40 -9
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +17 -1
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +42 -18
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
- package/dist/extensions/agent-browser/lib/parsing.js +0 -7
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +4 -1
- package/dist/extensions/agent-browser/lib/playbook.js +4 -10
- package/dist/extensions/agent-browser/lib/process-identity.js +0 -5
- package/dist/extensions/agent-browser/lib/process.js +12 -9
- package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -8
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +6 -7
- package/dist/extensions/agent-browser/lib/results/categories.js +1 -8
- package/dist/extensions/agent-browser/lib/results/confirmation.js +0 -7
- package/dist/extensions/agent-browser/lib/results/contracts.js +0 -7
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +0 -7
- package/dist/extensions/agent-browser/lib/results/envelope.js +0 -7
- package/dist/extensions/agent-browser/lib/results/network.js +0 -7
- package/dist/extensions/agent-browser/lib/results/next-actions.js +0 -30
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +1 -7
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation.js +0 -5
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot.js +0 -7
- package/dist/extensions/agent-browser/lib/results/text.js +0 -7
- package/dist/extensions/agent-browser/lib/runtime.js +4 -14
- package/dist/extensions/agent-browser/lib/session-page-state.js +0 -7
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/temp.js +0 -7
- package/dist/extensions/agent-browser/lib/web-search.js +0 -5
- package/docs/ARCHITECTURE.md +17 -16
- package/docs/COMMAND_REFERENCE.md +10 -9
- package/docs/ELECTRON.md +3 -19
- package/docs/RELEASE.md +5 -17
- package/docs/REQUIREMENTS.md +4 -4
- package/docs/SUPPORT_MATRIX.md +18 -17
- package/docs/TOOL_CONTRACT.md +23 -19
- package/docs/platform-smoke.md +1 -1
- package/package.json +5 -6
- package/scripts/doctor.mjs +1 -1
- package/dist/extensions/agent-browser/lib/electron/text.js +0 -13
- package/dist/extensions/agent-browser/lib/input-modes.js +0 -10
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +0 -5
- package/dist/extensions/agent-browser/lib/results/artifact-state.js +0 -13
- package/dist/extensions/agent-browser/lib/results.js +0 -14
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import { LAUNCH_SCOPED_FLAG_LABEL } from "./launch-scoped-flags.js";
|
|
2
|
-
/**
|
|
3
|
-
* Purpose: Provide the canonical agent_browser operating playbook shared by runtime prompt metadata and generated documentation fragments.
|
|
4
|
-
* Responsibilities: Define stable guidance bullets, native tool-call examples, and wrapper-behavior notes without importing runtime/browser process code.
|
|
5
|
-
* Scope: Agent-facing documentation and prompt-guidance text only; command execution and wrapper state behavior live in runtime modules.
|
|
6
|
-
* Usage: Imported by the extension entrypoint for promptGuidelines and by the documentation drift-check script for generated Markdown blocks.
|
|
7
|
-
* Invariants/Assumptions: The native pi tool receives args after the agent-browser binary, stdin is only for batch/eval --stdin/auth save --password-stdin, and wrapper behavior documented here must match implemented behavior.
|
|
8
|
-
*/
|
|
9
2
|
export const PROJECT_RULE_PROMPT = "Project rule: when browser automation is needed, prefer the native `agent_browser` tool. Do not run direct `agent-browser` bash commands unless the user explicitly asks for a bash-oriented workflow or browser-integration debugging.";
|
|
10
3
|
export const TOOL_PROMPT_GUIDELINES_PREFIX = [
|
|
11
4
|
"Use agent_browser for real browser or live web content.",
|
|
@@ -21,7 +14,7 @@ export const QUICK_START_GUIDELINES = [
|
|
|
21
14
|
`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.`,
|
|
22
15
|
"Constrained job navigation is explicit only: click (and select/submit flows that may navigate) does not prove the next page loaded; add assertUrl and/or assertText after navigation-prone steps before screenshot or later interactions. 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.",
|
|
23
16
|
"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 launching Chrome; 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.",
|
|
24
|
-
"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. 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. 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.",
|
|
17
|
+
"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. 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.",
|
|
25
18
|
"When details.nextActions is present, prefer those exact native agent_browser follow-up payloads over prose guidance; they may include args, stdin, sessionMode, networkSourceLookup, safety notes, or artifactPath for saved files.",
|
|
26
19
|
];
|
|
27
20
|
export const WEB_SEARCH_PROMPT_GUIDELINE = "Use agent_browser_web_search for quick live search/URL discovery; prefer it over public search-engine forms that can hit anti-bot/CAPTCHA-gated pages. Use agent_browser after you have a target URL; one query, one follow-up max; stop on HTTP 429.";
|
|
@@ -37,13 +30,14 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
37
30
|
"Do not invent fixed explicit session names for routine tasks. Use the implicit session unless you truly need multiple isolated browser sessions in the same conversation.",
|
|
38
31
|
`When using launch-scoped flags (${LAUNCH_SCOPED_FLAG_LABEL}), put them on the first command for that session. If you intentionally use an explicit --session, keep using that same explicit session for follow-ups.`,
|
|
39
32
|
"Caller-owned explicit sessions are serialized per effective canonical namespace/session inside this extension while live URL checks, semantic-action snapshots, and the requested command run. For raw batches whose later content step depends on navigation, use exact batch --bail or split the calls; unsafe continue-after-navigation-failure shapes are rejected before the batch runs.",
|
|
33
|
+
"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, omits local-launch-only `--args` / `--allow-file-access` defaults from follow-up and cleanup subprocesses so upstream keeps the existing CDP connection, and live-checks the URL before later page reads/interactions because an attached browser can drift externally; a successful close clears the marker. First-use attach plus content calls are blocked until the URL is verified.",
|
|
40
34
|
`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.`,
|
|
41
35
|
"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.",
|
|
42
36
|
"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.",
|
|
43
37
|
"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.",
|
|
44
38
|
"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.",
|
|
45
39
|
"For stateful browser context work, prefer purpose-specific page actions before dumping browser data: use auth save --password-stdin with the tool stdin field for credentials, auth list/show/delete/remove for local auth-profile maintenance, auth login when you need the browser to fill a saved profile, state save/load for portable test state, state list/show/rename and targeted `state clear <caller-owned-name>` for saved-state lifecycle cleanup (the native wrapper blocks broad clear/clean and managed targets), cookies get/set/clear and storage local|session only when the task needs those values, and expect cookie/storage/auth/state summaries to redact credential-like fields while allowing benign primitive storage values when useful for local QA.",
|
|
46
|
-
"Upstream restore sessions periodically autosave cookies and localStorage while the browser stays open, including page-driven background changes; AGENT_BROWSER_AUTOSAVE_INTERVAL_MS controls the interval (30000 by default; 0 disables periodic saves but keeps save-on-close), while the never value for --restore-save disables automatic saves for that restore session.",
|
|
40
|
+
"Upstream restore sessions periodically autosave cookies and localStorage while the browser stays open, including page-driven background changes; AGENT_BROWSER_AUTOSAVE_INTERVAL_MS controls the interval (30000 by default; 0 disables periodic saves but keeps save-on-close), while the never value for --restore-save disables automatic saves for that restore session. For wrapper-owned headed launches, the wrapper defaults the interval to 0 because upstream 0.33.2 collects multi-origin storage through visible temporary tabs, then records and reapplies the effective launch-time value to helpers and follow-ups so daemon configuration remains stable. Native close still saves, but direct window close can lose newer state because upstream exempts headed browsers from idle shutdown; set AGENT_BROWSER_AUTOSAVE_INTERVAL_MS before launch when periodic preservation matters, because changing it on a running wrapper-owned headed session requires close plus a fresh launch.",
|
|
47
41
|
"For batch chains that touch cookies, storage, auth, or other secret-bearing commands, use details.batchSteps for per-step artifacts, categories, spill paths, and full structured errors; top-level details.data on batch is only a compact redacted step matrix (success, argv-redacted command, redacted result or scrubbed error text) built from the same presentation rules as standalone calls.",
|
|
48
42
|
"For non-core families, pass current upstream commands through the native tool directly: network requests, network route <url>, network har start/stop [path], diff snapshot, diff screenshot --baseline <file>, diff url <u1> <u2>, trace start, trace stop [path], profiler start, profiler stop [path], record start <path>, record stop, console/errors [--clear], highlight <selector>, inspect, clipboard read, clipboard write <text>, clipboard copy/paste, stream enable/disable/status, dashboard start/stop, device list for iOS simulator inventory, and chat <message>. For compact network requests output, prefer details.nextActions for request detail, route-mock diagnostics, actionable failed-request networkSourceLookup, filtering, clearing the aggregate buffer before repro, or HAR capture follow-ups instead of guessing request-id syntax. Artifact-producing commands report details.artifacts and verification state; long-running starts such as stream, dashboard, trace/profiler, and record should be paired with the matching stop/disable command when the task is done; stream enable already-enabled outcomes are treated as idempotent success with status/disable follow-ups.",
|
|
49
43
|
"For Electron desktop apps, prefer top-level electron for wrapper-owned discovery, isolated launch, status, compact probe, and cleanup: list first, treat likely-sensitive annotations as hints rather than enforcement, launch with the default snapshot handoff unless handoff: \"tabs\" is the safer diagnostic starting point, use electron.probe or snapshot -i/qa.attached for current-session state, and always cleanup the returned launchId when done. electron.launch uses an isolated temporary profile; it does not reuse the app's normal signed-in profile or attach to an already-running authenticated app. For signed-in local app state, host-launch the normal app with --remote-debugging-port when appropriate, then use raw args connect <port|url>; after connect, run get url to verify the active target before page-content reads, inspect tab list, select the stable tab id such as tab t2, verify it again with get url, then run a condition wait or snapshot -i before using refs. close commands (`close`, `quit`, or `exit`) only close the browser/CDP session; leave manually launched app shutdown, profile cleanup, and explicit artifacts to the host owner.",
|
|
@@ -55,7 +49,7 @@ export const SHARED_BROWSER_PLAYBOOK_GUIDELINES = [
|
|
|
55
49
|
"For downloads, prefer download <selector> <path> when an element click should save a file; simple loopback anchor downloads are saved to the requested path when the wrapper can resolve an HTTP(S) href. Do not rely on click alone when you need the downloaded file on disk.",
|
|
56
50
|
"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 fall back to type, press Enter/arrow keys, or visible option refs.",
|
|
57
51
|
"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.",
|
|
58
|
-
"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. If a function-shaped snippet returns {}, details.evalStdinHint may warn that the function was serialized instead of called. The native wrapper blocks follow-up inspection, scripting, and interaction on file:// pages to protect authenticated local browser state; use a reachable HTTP(S) fixture instead. 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.",
|
|
52
|
+
"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. The native wrapper blocks follow-up inspection, scripting, and interaction on file:// pages to protect authenticated local browser state; use a reachable HTTP(S) fixture instead. 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.",
|
|
59
53
|
"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.",
|
|
60
54
|
"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.",
|
|
61
55
|
"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.",
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Describe the native command used to read a process start identity for PID-reuse-safe ownership checks.
|
|
3
|
-
* Responsibilities: Keep POSIX and native-Windows process identity probes aligned across policy locks and temp cleanup.
|
|
4
|
-
* Scope: Command construction, output normalization, and cached async lookup for the current process.
|
|
5
|
-
*/
|
|
6
1
|
import { execFile } from "node:child_process";
|
|
7
2
|
import { win32 } from "node:path";
|
|
8
3
|
const WINDOWS_PROCESS_START_IDENTITY_PREFIX = "win32-powershell-ticks-v1:";
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Purpose: Execute the upstream agent-browser binary for the pi-agent-browser extension.
|
|
3
|
-
* Responsibilities: Validate POSIX socket storage, spawn the agent-browser subprocess, forward parent environment variables plus wrapper overrides, stream optional stdin, bound in-memory output buffering, spill oversized stdout safely to a private temp file under a disk budget, and honor abort signals.
|
|
4
|
-
* Scope: Process execution only; argument planning, output formatting, and pi tool registration live elsewhere.
|
|
5
|
-
* Usage: Called by the extension tool after argument validation and session planning are complete.
|
|
6
|
-
* Invariants/Assumptions: The binary name is always `agent-browser`; Windows routes through PowerShell to invoke npm launchers with escaped argv; callers handle semantic success/error interpretation.
|
|
7
|
-
*/
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
2
|
import { spawn } from "node:child_process";
|
|
9
3
|
import { lstat, mkdir, readdir } from "node:fs/promises";
|
|
10
4
|
import { dirname, isAbsolute, join } from "node:path";
|
|
@@ -32,6 +26,10 @@ const DEFAULT_AGENT_BROWSER_PROCESS_TIMEOUT_MS = 35_000;
|
|
|
32
26
|
/** Grace period after `exit` before resolving when `close` is delayed by inherited stdio handles. */
|
|
33
27
|
const EXIT_STDIO_GRACE_MS = 100;
|
|
34
28
|
const WINDOWS_AGENT_BROWSER_MISSING_MARKER = "PI_AGENT_BROWSER_COMMAND_NOT_FOUND:agent-browser.cmd";
|
|
29
|
+
const attachedBrowserSessionContext = new AsyncLocalStorage();
|
|
30
|
+
export function withAttachedBrowserSessionContext(preserve, run) {
|
|
31
|
+
return attachedBrowserSessionContext.run(preserve || attachedBrowserSessionContext.getStore() === true, run);
|
|
32
|
+
}
|
|
35
33
|
function appendTail(text, addition, maxChars) {
|
|
36
34
|
const combined = text + addition;
|
|
37
35
|
return combined.length <= maxChars ? combined : combined.slice(combined.length - maxChars);
|
|
@@ -87,7 +85,7 @@ export function reorderWindowsLeadingGlobalArgs(args) {
|
|
|
87
85
|
}
|
|
88
86
|
return args;
|
|
89
87
|
}
|
|
90
|
-
export function pinAgentBrowserFileAccessDisabled(args, wrapperCompatibilityUserAgent) {
|
|
88
|
+
export function pinAgentBrowserFileAccessDisabled(args, wrapperCompatibilityUserAgent, preserveAttachedBrowserSession = false) {
|
|
91
89
|
const filtered = [];
|
|
92
90
|
for (let index = 0; index < args.length; index += 1) {
|
|
93
91
|
const token = args[index];
|
|
@@ -100,6 +98,9 @@ export function pinAgentBrowserFileAccessDisabled(args, wrapperCompatibilityUser
|
|
|
100
98
|
}
|
|
101
99
|
filtered.push(token);
|
|
102
100
|
}
|
|
101
|
+
// These are launch-only controls. Sending them on an attached-session follow-up makes upstream replace the CDP connection with a local browser.
|
|
102
|
+
if (preserveAttachedBrowserSession)
|
|
103
|
+
return filtered;
|
|
103
104
|
// Upstream's flag overrides only the active CDP target; the Chrome arg covers new tabs. Its --args parser splits commas/newlines.
|
|
104
105
|
const browserArgs = wrapperCompatibilityUserAgent
|
|
105
106
|
? `--user-agent=${wrapperCompatibilityUserAgent.replaceAll(/[\r\n,]/g, "")}`
|
|
@@ -333,6 +334,7 @@ function getManagedPreSpawnPolicyError(options, effectiveEnv, allowManagedSessio
|
|
|
333
334
|
}
|
|
334
335
|
export async function runAgentBrowserProcess(options) {
|
|
335
336
|
const { allowManagedSessionTarget, cwd, env, managedSessionRestoreState, managedStateCurrentPageUrl, managedStatePageUrlUnknown, signal, stdin, trustedFirstBatchTabSelection } = options;
|
|
337
|
+
const preserveAttachedBrowserSession = options.preserveAttachedBrowserSession === true || attachedBrowserSessionContext.getStore() === true;
|
|
336
338
|
const ownedManagedSession = options.ownedManagedSession === true || isOwnedManagedSessionTarget(options.args);
|
|
337
339
|
const args = canonicalizeOwnedManagedSessionCloseArgs({
|
|
338
340
|
args: options.args,
|
|
@@ -390,6 +392,7 @@ export async function runAgentBrowserProcess(options) {
|
|
|
390
392
|
...getManagedSessionRestoreProtectedEnv(managedSessionRestoreOptions, managedSessionRestoreEnv),
|
|
391
393
|
...getOwnedManagedSessionNamespaceEnv(managedSessionRestoreOptions),
|
|
392
394
|
...ownedManagedSessionCompatibilityEnv,
|
|
395
|
+
AGENT_BROWSER_ALLOW_FILE_ACCESS: undefined,
|
|
393
396
|
[AGENT_BROWSER_ARGS_ENV]: undefined,
|
|
394
397
|
};
|
|
395
398
|
const explicitSocketDir = processOverrides[AGENT_BROWSER_SOCKET_DIR_ENV];
|
|
@@ -524,7 +527,7 @@ export async function runAgentBrowserProcess(options) {
|
|
|
524
527
|
resolve({ aborted: false, agentBrowserStarted: false, exitCode: 1, spawnError: new Error(spawnPolicyError), stderr: "", stdout: "", timedOut: false });
|
|
525
528
|
return;
|
|
526
529
|
}
|
|
527
|
-
const spawnCommand = buildAgentBrowserSpawnCommand(pinAgentBrowserFileAccessDisabled(args, ownedManagedSessionCompatibilityEnv.AGENT_BROWSER_USER_AGENT));
|
|
530
|
+
const spawnCommand = buildAgentBrowserSpawnCommand(pinAgentBrowserFileAccessDisabled(args, ownedManagedSessionCompatibilityEnv.AGENT_BROWSER_USER_AGENT, preserveAttachedBrowserSession));
|
|
528
531
|
const child = spawn(spawnCommand.command, spawnCommand.args, {
|
|
529
532
|
cwd,
|
|
530
533
|
env: childEnv,
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Derive operator prompt constraints for browser-run preflight guards and legacy bash policy.
|
|
3
|
-
* Responsibilities: Parse the latest user message into requested artifact paths and legacy bash allowance.
|
|
4
|
-
* Scope: Pure prompt-text policy; enforcement lives in orchestration prompt-guards and the extension entrypoint.
|
|
5
|
-
*/
|
|
6
1
|
const BROWSER_PROMPT_PATTERNS = [
|
|
7
2
|
/\b(?:agent[_ -]?browser|browser automation|eval\s+--stdin|screenshot|snapshot|tab\s+list)\b/i,
|
|
8
3
|
/\b(?:react\s+(?:tree|inspect|renders|suspense)|web\s+vitals|core\s+web\s+vitals|pushstate)\b/i,
|
|
@@ -17,41 +12,190 @@ const LEGACY_BASH_ALLOW_PATTERNS = [
|
|
|
17
12
|
/\bagent-browser\s+--(?:help|version)\b/i,
|
|
18
13
|
/\bdebug(?:ging)?\b.*\b(?:agent[_ -]?browser|agent_browser|browser integration)\b/i,
|
|
19
14
|
];
|
|
20
|
-
const PROMPT_ARTIFACT_PATH_PATTERN = /(?:^|[\s"'`(:])((?:\/[^\s"'`),;]+|[A-Za-z]:[\\/][^\s"'`),;]+|\.{1,2}[\\/][^\s"'`),;]+|[^\s"'`),;:\\/]+(?:[\\/][^\s"'`)
|
|
21
|
-
|
|
15
|
+
const PROMPT_ARTIFACT_PATH_PATTERN = /(?:^|[\s"'`(:])((?:\/[^\s"'`),;]+|[A-Za-z]:[\\/][^\s"'`),;]+|\.{1,2}[\\/][^\s"'`),;]+|[^\s"'`()[\],;:\\/]+(?:[\\/][^\s"'`()[\],;\\/]+)+|[^\s"'`()[\],;:\\/]+)\.(?:png|jpe?g|webp|gif|webm|mp4|har|pdf|trace|json))(?=[\s"'`),;.!?]|$)/gi;
|
|
16
|
+
const PROMPT_ARTIFACT_COLON_OUTPUT_INTENT_PATTERN = /\b(?:capture|create|export|generate|output|record|render|save|screenshot|start|take|write)\s+(?:(?:a|an|another|the)\s+)?(?:short\s+)?(?:(?:full[- ]page|page|screen)\s+)?(?:image|page|recordings?|screenshots?|screen|video)\s*:\s*$/i;
|
|
17
|
+
const PROMPT_ARTIFACT_OUTPUT_INTENT_PATTERN = /\b(?:capture|create|export|generate|output|record|render|save|screenshot|start|take|write)\s+(?:(?:a|an|another|the|this)\s+)?(?:short\s+)?(?:(?:full[- ]page|page|screen)\s+)?(?:image|page|recordings?|screenshots?|screen|video)\s+(?:directly\s+)?(?:\b(?:at|as|to)\b\s*[:=-]?|\bhere\b(?:\s+(?:if|when)\s+(?:recordings?\s+)?(?:(?:are|is)\s+)?available)?\s*[:=-]?)\s*$|\b(?:export|output|save|write)\s+(?:it\s+)?(?:at|as|to)\s*[:=-]?\s*$/i;
|
|
18
|
+
const PROMPT_ARTIFACT_UNSAFE_OUTPUT_PREFIX_PATTERN = /n['’]t\b|\b(?:cannot|disallowed|forbidden|maybe|needed|never|no|not|optional(?:ly)?|perhaps|prohibited|rather|refrain|unable|without)\b|\b(?:he|i|it|she|they|we|you)\s+(?:can|could|may|might)\b/i;
|
|
19
|
+
const PROMPT_ARTIFACT_AFFIRMATIVE_PREFIX_PATTERN = /^(?:(?:and(?:\s+then)?|then)(?:\s+please)?|please|you|(?:be|make)\s+sure\s+to|(?:can|could|will|would)\s+you(?:\s+please)?|(?:i|we)\s+(?:need|want)\s+you\s+to|you\s+(?:must|should))$/i;
|
|
20
|
+
const PROMPT_ARTIFACT_AFFIRMATIVE_LEAD_IN_PATTERN = /^(?:please\s+)?(?:capture|create|export|generate|output|record|render|save|screenshot|start|take|write)\b.*(?:[,\-:–—]|\b(?:and|then))\s*$/i;
|
|
21
|
+
const PROMPT_ARTIFACT_LIST_CONNECTOR_PATTERN = /^[\s"'`()\[\]{},;:.*!?\/&+>-]*(?:(?:and|or)[\s"'`()\[\]{},;:.*!?\/&+>-]*)?$/i;
|
|
22
|
+
const PROMPT_ARTIFACT_LIST_PREFIX_PATTERN = /^\s*(?:(?:[-*+]|\d+[.)])\s*)/;
|
|
23
|
+
const PROMPT_ARTIFACT_OPTIONAL_RECORDING_PATTERN = /\b(?:if|when)\s+(?:recordings?\s+)?(?:(?:are|is)\s+)?available\b/i;
|
|
24
|
+
const PROMPT_ARTIFACT_STANDALONE_OPTIONAL_RECORDING_PATTERN = /^\s*(?:if|when)\s+(?:recordings?\s+)?(?:(?:are|is)\s+)?available[.:;]?\s*$/i;
|
|
25
|
+
const PROMPT_ARTIFACT_EXPLICIT_OPTIONAL_PATTERN = /\b(?:optionally|if\s+(?:convenient|desired|needed|possible|you\s+can|you\s+want\s+to)|when\s+(?:convenient|desired|needed|possible)|only\s+if\s+you\s+(?:can|want\s+to))\b/i;
|
|
26
|
+
const PROMPT_ARTIFACT_REFERENCE_INTENT_PATTERN = /\btake\s+the\s+(?:image|recording|screenshot|video)\s+(?:at|from)\b/i;
|
|
27
|
+
const PROMPT_ARTIFACT_CLAUSE_BOUNDARY_PATTERN = /(?:[;.!?](?:\s|$)|\b(?:but|instead)\b)/i;
|
|
28
|
+
function getPromptArtifactKind(path) {
|
|
22
29
|
const lowerPath = path.toLowerCase();
|
|
23
30
|
if (/\.(?:webm|mp4)$/.test(lowerPath))
|
|
24
31
|
return "recording";
|
|
25
32
|
if (/\.(?:png|jpe?g|webp|gif)$/.test(lowerPath))
|
|
26
33
|
return "screenshot";
|
|
27
|
-
const lowerLine = line.toLowerCase();
|
|
28
|
-
if (lowerLine.includes("screenshot"))
|
|
29
|
-
return "screenshot";
|
|
30
|
-
if (/\b(?:screen\s+recording|recording|webm|video)\b/.test(lowerLine))
|
|
31
|
-
return "recording";
|
|
32
34
|
return undefined;
|
|
33
35
|
}
|
|
36
|
+
function getPromptArtifactIntentClause(context) {
|
|
37
|
+
return context.split(PROMPT_ARTIFACT_CLAUSE_BOUNDARY_PATTERN).at(-1) ?? context;
|
|
38
|
+
}
|
|
39
|
+
function getPromptArtifactTrailingClause(context) {
|
|
40
|
+
return context.split(PROMPT_ARTIFACT_CLAUSE_BOUNDARY_PATTERN)[0] ?? context;
|
|
41
|
+
}
|
|
42
|
+
function hasPromptArtifactOutputIntent(context) {
|
|
43
|
+
const clause = getPromptArtifactIntentClause(context)
|
|
44
|
+
.replace(/\[[^\]\r\n]*\]\(\s*$/, "")
|
|
45
|
+
.replace(/[([{\"'`]+\s*$/, "");
|
|
46
|
+
const intentMatch = clause.match(PROMPT_ARTIFACT_OUTPUT_INTENT_PATTERN) ?? clause.match(PROMPT_ARTIFACT_COLON_OUTPUT_INTENT_PATTERN);
|
|
47
|
+
if (!intentMatch)
|
|
48
|
+
return false;
|
|
49
|
+
const prefix = clause.slice(0, intentMatch.index ?? 0);
|
|
50
|
+
const governingPrefix = prefix.split(/\b(?:before|unless|until)\b/i).at(-1) ?? prefix;
|
|
51
|
+
if (PROMPT_ARTIFACT_UNSAFE_OUTPUT_PREFIX_PATTERN.test(governingPrefix))
|
|
52
|
+
return false;
|
|
53
|
+
const normalizedPrefix = governingPrefix.trim();
|
|
54
|
+
return normalizedPrefix.length === 0
|
|
55
|
+
|| PROMPT_ARTIFACT_AFFIRMATIVE_PREFIX_PATTERN.test(normalizedPrefix)
|
|
56
|
+
|| PROMPT_ARTIFACT_AFFIRMATIVE_LEAD_IN_PATTERN.test(governingPrefix);
|
|
57
|
+
}
|
|
58
|
+
function stripPromptArtifactListPrefix(context) {
|
|
59
|
+
return context.replace(PROMPT_ARTIFACT_LIST_PREFIX_PATTERN, "");
|
|
60
|
+
}
|
|
61
|
+
function normalizePromptArtifactPath(path) {
|
|
62
|
+
return path.replace(/^[([{]+/, "");
|
|
63
|
+
}
|
|
64
|
+
function isLikelyInboundPromptArtifact(path) {
|
|
65
|
+
return /(?:^|[\\/])pi-(?:attachment|clipboard|paste|upload)-/i.test(path);
|
|
66
|
+
}
|
|
34
67
|
function extractPromptRequestedArtifacts(prompt) {
|
|
35
68
|
const artifacts = [];
|
|
36
|
-
const seen = new
|
|
37
|
-
|
|
69
|
+
const seen = new Map();
|
|
70
|
+
const lines = prompt.split(/\r?\n/);
|
|
71
|
+
let inCodeFence = false;
|
|
72
|
+
let listArtifactIndexes = [];
|
|
73
|
+
let listContinuation;
|
|
74
|
+
let pendingRecordingAvailability = false;
|
|
75
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
76
|
+
const line = lines[lineIndex] ?? "";
|
|
77
|
+
if (/^\s*(?:`{3,}|~{3,})/.test(line)) {
|
|
78
|
+
inCodeFence = !inCodeFence;
|
|
79
|
+
listArtifactIndexes = [];
|
|
80
|
+
listContinuation = undefined;
|
|
81
|
+
pendingRecordingAvailability = false;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (inCodeFence)
|
|
85
|
+
continue;
|
|
38
86
|
PROMPT_ARTIFACT_PATH_PATTERN.lastIndex = 0;
|
|
87
|
+
const pathMatches = [];
|
|
39
88
|
for (const match of line.matchAll(PROMPT_ARTIFACT_PATH_PATTERN)) {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const kind = inferPromptArtifactKind(line, path);
|
|
44
|
-
if (!kind)
|
|
89
|
+
const rawPath = match[1]?.trim();
|
|
90
|
+
const path = rawPath ? normalizePromptArtifactPath(rawPath) : undefined;
|
|
91
|
+
if (!path || !rawPath)
|
|
45
92
|
continue;
|
|
46
|
-
const
|
|
47
|
-
|
|
93
|
+
const start = (match.index ?? 0) + match[0].indexOf(rawPath) + rawPath.indexOf(path);
|
|
94
|
+
pathMatches.push({ end: start + path.length, path, start });
|
|
95
|
+
}
|
|
96
|
+
if (pathMatches.length === 0 && PROMPT_ARTIFACT_STANDALONE_OPTIONAL_RECORDING_PATTERN.test(line)) {
|
|
97
|
+
if (listContinuation?.kind === "recording") {
|
|
98
|
+
for (const artifactIndex of listArtifactIndexes) {
|
|
99
|
+
if (artifacts[artifactIndex]?.kind === "recording")
|
|
100
|
+
artifacts[artifactIndex].required = false;
|
|
101
|
+
}
|
|
102
|
+
listContinuation = { ...listContinuation, required: false };
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
pendingRecordingAvailability = true;
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (pathMatches.length === 0 && pendingRecordingAvailability && /\brecordings?\b/i.test(line) && hasPromptArtifactOutputIntent(line))
|
|
110
|
+
continue;
|
|
111
|
+
const pendingRecordingAvailabilityForLine = pathMatches.length > 0 && pendingRecordingAvailability;
|
|
112
|
+
pendingRecordingAvailability = false;
|
|
113
|
+
const pathlessLine = stripPromptArtifactListPrefix(line.replace(PROMPT_ARTIFACT_PATH_PATTERN, ""));
|
|
114
|
+
const remainder = pathlessLine.replace(/[\s"'`()\[\],;:.*!?>-]+/g, "").toLowerCase();
|
|
115
|
+
const listSyntax = pathlessLine
|
|
116
|
+
.replace(PROMPT_ARTIFACT_OPTIONAL_RECORDING_PATTERN, "")
|
|
117
|
+
.replace(PROMPT_ARTIFACT_EXPLICIT_OPTIONAL_PATTERN, "");
|
|
118
|
+
const isPathList = pathMatches.length > 0 && PROMPT_ARTIFACT_LIST_CONNECTOR_PATTERN.test(listSyntax);
|
|
119
|
+
const groupOptional = new Map();
|
|
120
|
+
const candidates = [];
|
|
121
|
+
let nextGroup = 0;
|
|
122
|
+
let previousGroup;
|
|
123
|
+
let previousKind;
|
|
124
|
+
let previousPathEnd = 0;
|
|
125
|
+
for (let matchIndex = 0; matchIndex < pathMatches.length; matchIndex += 1) {
|
|
126
|
+
const { end, path, start } = pathMatches[matchIndex];
|
|
127
|
+
const localContext = stripPromptArtifactListPrefix(line.slice(previousPathEnd, start));
|
|
128
|
+
const intentContext = matchIndex === 0 && (isPathList || !remainder || ["file", "output", "path"].includes(remainder))
|
|
129
|
+
? `${lines[lineIndex - 1] ?? ""}\n${localContext}`
|
|
130
|
+
: localContext;
|
|
131
|
+
const kind = getPromptArtifactKind(path);
|
|
132
|
+
const directIntent = hasPromptArtifactOutputIntent(intentContext);
|
|
133
|
+
const sameLineContinuation = kind === previousKind && previousGroup !== undefined && PROMPT_ARTIFACT_LIST_CONNECTOR_PATTERN.test(localContext);
|
|
134
|
+
const priorLineContinuation = matchIndex === 0 && isPathList && kind === listContinuation?.kind && PROMPT_ARTIFACT_LIST_CONNECTOR_PATTERN.test(localContext);
|
|
135
|
+
let group;
|
|
136
|
+
if (directIntent) {
|
|
137
|
+
group = nextGroup++;
|
|
138
|
+
groupOptional.set(group, pendingRecordingAvailabilityForLine || PROMPT_ARTIFACT_OPTIONAL_RECORDING_PATTERN.test(getPromptArtifactIntentClause(intentContext)));
|
|
139
|
+
}
|
|
140
|
+
else if (sameLineContinuation) {
|
|
141
|
+
group = previousGroup;
|
|
142
|
+
}
|
|
143
|
+
else if (priorLineContinuation) {
|
|
144
|
+
group = nextGroup++;
|
|
145
|
+
groupOptional.set(group, listContinuation?.required === false);
|
|
146
|
+
}
|
|
147
|
+
const referenceReading = directIntent && PROMPT_ARTIFACT_REFERENCE_INTENT_PATTERN.test(getPromptArtifactIntentClause(intentContext));
|
|
148
|
+
const trailingContext = getPromptArtifactTrailingClause(line.slice(end, pathMatches[matchIndex + 1]?.start ?? line.length));
|
|
149
|
+
const explicitlyOptional = directIntent && (PROMPT_ARTIFACT_EXPLICIT_OPTIONAL_PATTERN.test(getPromptArtifactIntentClause(intentContext))
|
|
150
|
+
|| PROMPT_ARTIFACT_EXPLICIT_OPTIONAL_PATTERN.test(trailingContext));
|
|
151
|
+
if (!kind || group === undefined || referenceReading || explicitlyOptional || isLikelyInboundPromptArtifact(path)) {
|
|
152
|
+
previousGroup = undefined;
|
|
153
|
+
previousKind = undefined;
|
|
154
|
+
previousPathEnd = end;
|
|
48
155
|
continue;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
156
|
+
}
|
|
157
|
+
candidates.push({ continuedFromPriorLine: priorLineContinuation && !directIntent, group, kind, matchIndex, path });
|
|
158
|
+
previousGroup = group;
|
|
159
|
+
previousKind = kind;
|
|
160
|
+
previousPathEnd = end;
|
|
161
|
+
}
|
|
162
|
+
for (const candidate of candidates) {
|
|
163
|
+
const match = pathMatches[candidate.matchIndex];
|
|
164
|
+
const nextStart = pathMatches[candidate.matchIndex + 1]?.start ?? line.length;
|
|
165
|
+
if (PROMPT_ARTIFACT_OPTIONAL_RECORDING_PATTERN.test(getPromptArtifactTrailingClause(line.slice(match.end, nextStart)))) {
|
|
166
|
+
groupOptional.set(candidate.group, true);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const lineArtifactIndexes = [];
|
|
170
|
+
for (const candidate of candidates) {
|
|
171
|
+
const required = candidate.kind === "screenshot" || groupOptional.get(candidate.group) !== true;
|
|
172
|
+
const key = `${candidate.kind}:${candidate.path}`;
|
|
173
|
+
let artifactIndex = seen.get(key);
|
|
174
|
+
if (artifactIndex === undefined) {
|
|
175
|
+
artifactIndex = artifacts.length;
|
|
176
|
+
seen.set(key, artifactIndex);
|
|
177
|
+
artifacts.push({ kind: candidate.kind, path: candidate.path, required });
|
|
178
|
+
}
|
|
179
|
+
else if (required) {
|
|
180
|
+
artifacts[artifactIndex].required = true;
|
|
181
|
+
}
|
|
182
|
+
lineArtifactIndexes.push(artifactIndex);
|
|
183
|
+
}
|
|
184
|
+
const lastCandidate = candidates.at(-1);
|
|
185
|
+
if (isPathList && lastCandidate?.matchIndex === pathMatches.length - 1) {
|
|
186
|
+
const lastArtifact = artifacts[lineArtifactIndexes.at(-1)];
|
|
187
|
+
listContinuation = { kind: lastArtifact.kind, required: lastArtifact.required };
|
|
188
|
+
if (candidates[0]?.continuedFromPriorLine) {
|
|
189
|
+
for (const artifactIndex of lineArtifactIndexes)
|
|
190
|
+
listArtifactIndexes.push(artifactIndex);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
listArtifactIndexes = lineArtifactIndexes;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
listArtifactIndexes = [];
|
|
198
|
+
listContinuation = undefined;
|
|
55
199
|
}
|
|
56
200
|
}
|
|
57
201
|
return artifacts;
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Build generic nextAction recommendations from result categories, artifacts, Electron lifecycle state, and recovery context.
|
|
3
|
-
* Responsibilities: Preserve stable action ids/order while keeping recommendation policy out of generic shared helpers.
|
|
4
|
-
* Scope: Generic result-level recommendations only; feature-specific diagnostics append their own actions in the extension entrypoint.
|
|
5
|
-
* Usage: Called by presentation and extension result assembly.
|
|
6
|
-
* Invariants/Assumptions: Action ids are public machine-readable contracts; preserve first-observed order.
|
|
7
|
-
*/
|
|
8
1
|
import { isOpenNavigationCommand, isPageMutationCommand } from "../command-taxonomy.js";
|
|
9
|
-
import { isPendingRecordingArtifact } from "./artifact-
|
|
2
|
+
import { isPendingRecordingArtifact } from "./artifact-manifest.js";
|
|
10
3
|
import { buildNextToolAction } from "./next-actions.js";
|
|
11
4
|
import { AGENT_BROWSER_RECOVERY_NEXT_ACTION_IDS, buildRecoveryNextActions, } from "./recovery-actions.js";
|
|
12
5
|
function buildArtifactAction(path) {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Own persistent session artifact manifest merge, retention, and validation logic.
|
|
3
|
-
* Responsibilities: Parse manifest bounds, recognize manifest entries, merge new artifact rows, and format retention summaries.
|
|
4
|
-
* Scope: Manifest accounting only; artifact detection and presentation live in presentation modules.
|
|
5
|
-
* Usage: Imported by presentation and snapshot artifact persistence paths.
|
|
6
|
-
* Invariants/Assumptions: Explicit-path artifacts are host-owned while persistent-session spill files are bounded by the manifest cap.
|
|
7
|
-
*/
|
|
8
1
|
import { isRecord } from "../parsing.js";
|
|
2
|
+
export function isPendingRecordingCommand(command, subcommand, kind) {
|
|
3
|
+
return command === "record" && (subcommand === "start" || subcommand === "restart") && kind === "video";
|
|
4
|
+
}
|
|
5
|
+
export function isPendingRecordingArtifact(artifact) {
|
|
6
|
+
return isPendingRecordingCommand(artifact.command, artifact.subcommand, artifact.kind);
|
|
7
|
+
}
|
|
9
8
|
export const SESSION_ARTIFACT_MANIFEST_VERSION = 1;
|
|
10
9
|
export const SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES_ENV = "PI_AGENT_BROWSER_SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES";
|
|
11
10
|
export const DEFAULT_SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES = 100;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Purpose: Classify successful and failed agent-browser outcomes into stable result categories.
|
|
3
|
-
* Responsibilities: Map artifacts, inspection calls, errors, refs, selectors, downloads, drift, and timeouts to small enums.
|
|
4
|
-
* Scope: Category policy only; next-action recommendations and presentation formatting live elsewhere.
|
|
5
|
-
* Usage: Called by presentation and extension result assembly before details are exposed to Pi.
|
|
6
|
-
* Invariants/Assumptions: Category strings are public machine-readable contracts covered by tests and docs.
|
|
7
|
-
*/
|
|
8
|
-
import { isPendingRecordingArtifact } from "./artifact-state.js";
|
|
1
|
+
import { isPendingRecordingArtifact } from "./artifact-manifest.js";
|
|
9
2
|
function hasUnverifiedFileArtifact(artifacts) {
|
|
10
3
|
return (artifacts ?? []).some((artifact) => !isPendingRecordingArtifact(artifact) && artifact.exists !== true);
|
|
11
4
|
}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Detect upstream guarded-action confirmation-needed result shapes without creating wrapper-owned confirmation state.
|
|
3
|
-
* Responsibilities: Recognize confirmation-required markers, extract the pending upstream confirmation id, and optionally surface a short upstream action label.
|
|
4
|
-
* Scope: Pure result-shape detection shared by presentation and error derivation; command execution, approval state, and redaction stay in their existing modules.
|
|
5
|
-
* Usage: Imported by result presentation to render recovery commands and by envelope error handling to avoid hiding actionable confirmation payloads behind generic failure text.
|
|
6
|
-
* Invariants/Assumptions: Detection must be conservative: a confirmation marker and a non-empty upstream id are both required before a result is treated as actionable.
|
|
7
|
-
*/
|
|
8
1
|
import { isRecord } from "../parsing.js";
|
|
9
2
|
const CONFIRMATION_REQUIRED_FIELD_NAMES = [
|
|
10
3
|
"confirmation_required",
|
|
@@ -1,8 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Define stable result-rendering data contracts shared across focused result modules.
|
|
3
|
-
* Responsibilities: Keep upstream envelope, presentation, artifact, category, and network shapes in one type-only surface.
|
|
4
|
-
* Scope: Types only; runtime classifiers, manifests, network rules, and text helpers live in neighboring modules.
|
|
5
|
-
* Usage: Imported with `import type` by result modules and re-exported by the public results facade.
|
|
6
|
-
* Invariants/Assumptions: This file has no runtime policy so adding fields cannot hide behavior in a catch-all module.
|
|
7
|
-
*/
|
|
8
1
|
export {};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Detect whether upstream ref metadata or snapshot text proves an element is editable.
|
|
3
|
-
* Responsibilities: Parse structured ref flags and snapshot-line contenteditable/editable markers into a conservative boolean/unknown signal.
|
|
4
|
-
* Scope: Editable evidence only; callers decide how to rank controls or build recovery actions from the signal.
|
|
5
|
-
* Usage: Imported by snapshot compaction and selector/fill recovery diagnostics.
|
|
6
|
-
* Invariants/Assumptions: Explicit false evidence wins over positive hints; unknown remains undefined.
|
|
7
|
-
*/
|
|
8
1
|
const EDITABLE_REF_EVIDENCE_KEYS = ["editable", "contentEditable", "contenteditable", "isContentEditable"];
|
|
9
2
|
const EDITABLE_FALSE_TEXT_PATTERN = /\b(?:contenteditable|editable)\s*=\s*["']?(?:false|0)["']?/i;
|
|
10
3
|
const EDITABLE_ASSIGNMENT_TEXT_PATTERN = /\b(contenteditable|editable)\s*=\s*("[^"]*"|'[^']*'|[^\s,\]]+)/gi;
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Parse upstream agent-browser output and turn failure envelopes into actionable error text.
|
|
3
|
-
* Responsibilities: Read inline or spilled stdout, parse observed JSON envelope shapes, normalize batch arrays, and extract the most useful error text from nested upstream failures.
|
|
4
|
-
* Scope: Envelope parsing and error derivation only; content rendering and snapshot compaction live in separate modules.
|
|
5
|
-
* Usage: Imported by the public `lib/results.ts` facade and by tests through that facade.
|
|
6
|
-
* Invariants/Assumptions: Upstream `agent-browser --json` responses follow the observed `{ success, data, error }` envelope shape or the array shape returned by `batch --json`.
|
|
7
|
-
*/
|
|
8
1
|
import { readFile } from "node:fs/promises";
|
|
9
2
|
import { isRecord } from "../parsing.js";
|
|
10
3
|
import { detectConfirmationRequired } from "./confirmation.js";
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Classify failed network requests into actionable vs benign diagnostics.
|
|
3
|
-
* Responsibilities: Recognize failed request rows, de-prioritize browser icon misses, and summarize failure counts.
|
|
4
|
-
* Scope: Network diagnostic classification only.
|
|
5
|
-
* Usage: QA preset analysis and presentation network summaries.
|
|
6
|
-
* Invariants/Assumptions: Browser favicon/apple-touch icon misses are warnings; API/document/script failures are actionable.
|
|
7
|
-
*/
|
|
8
1
|
import { isRecord } from "../parsing.js";
|
|
9
2
|
export function getStringRecordField(value, key) {
|
|
10
3
|
const field = value[key];
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Own machine-readable agent_browser next-action contracts and merge policy.
|
|
3
|
-
* Responsibilities: Define the stable nextAction shape, build basic argv follow-ups, and provide deterministic action-list collection helpers.
|
|
4
|
-
* Scope: Result follow-up action mechanics only; command-specific recovery and artifact policies live in neighboring modules.
|
|
5
|
-
* Usage: Imported by result presentation helpers and the extension entrypoint when attaching details.nextActions.
|
|
6
|
-
* Invariants/Assumptions: Action ids are stable machine-readable contracts; dedupe preserves first occurrence order.
|
|
7
|
-
*/
|
|
8
1
|
export function withOptionalNamespaceArgs(namespace, args) {
|
|
9
2
|
return namespace && args[0] !== "--namespace" ? ["--namespace", namespace, ...args] : args;
|
|
10
3
|
}
|
|
@@ -65,26 +58,3 @@ export function alignPageChangeSummaryNextActionIds(summary, nextActions) {
|
|
|
65
58
|
const alignedIds = summary.nextActionIds.filter((id) => nextActionIds.has(id));
|
|
66
59
|
return alignedIds.length > 0 ? { ...summary, nextActionIds: alignedIds } : { ...summary, nextActionIds: undefined };
|
|
67
60
|
}
|
|
68
|
-
export class AgentBrowserNextActionCollector {
|
|
69
|
-
actions;
|
|
70
|
-
constructor(initialActions = undefined) {
|
|
71
|
-
this.actions = initialActions ? [...initialActions] : [];
|
|
72
|
-
}
|
|
73
|
-
append(actions) {
|
|
74
|
-
if (!actions || actions.length === 0)
|
|
75
|
-
return;
|
|
76
|
-
this.actions.push(...actions);
|
|
77
|
-
}
|
|
78
|
-
appendUnique(actions) {
|
|
79
|
-
appendUniqueAgentBrowserNextActions(this.actions, actions);
|
|
80
|
-
}
|
|
81
|
-
replace(actions) {
|
|
82
|
-
this.actions = actions ? [...actions] : [];
|
|
83
|
-
}
|
|
84
|
-
removeWhere(predicate) {
|
|
85
|
-
this.actions = this.actions.filter((action) => !predicate(action));
|
|
86
|
-
}
|
|
87
|
-
toArray() {
|
|
88
|
-
return this.actions.length > 0 ? [...this.actions] : undefined;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Own file artifact detection, verification, manifest merging, and inline image attachment for tool presentation.
|
|
3
|
-
* Responsibilities: Build artifact metadata, verification summaries, saved-file details, artifact retention notices, and safe image content.
|
|
4
|
-
* Scope: Artifact and image presentation only.
|
|
5
|
-
*/
|
|
6
1
|
import { readFile, stat } from "node:fs/promises";
|
|
7
2
|
import { extname, resolve } from "node:path";
|
|
8
3
|
import { isRecord, parsePositiveInteger } from "../../parsing.js";
|
|
9
|
-
import { formatSessionArtifactRetentionSummary, mergeSessionArtifactManifest, } from "../artifact-manifest.js";
|
|
10
|
-
import { isPendingRecordingArtifact, isPendingRecordingCommand } from "../artifact-state.js";
|
|
4
|
+
import { formatSessionArtifactRetentionSummary, isPendingRecordingArtifact, isPendingRecordingCommand, mergeSessionArtifactManifest, } from "../artifact-manifest.js";
|
|
11
5
|
import { classifyAgentBrowserSuccessCategory } from "../categories.js";
|
|
12
6
|
const IMAGE_EXTENSION_TO_MIME_TYPE = {
|
|
13
7
|
".gif": "image/gif",
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Build browser profile/config recovery hints for launch/setup failures.
|
|
3
|
-
* Responsibilities: Recognize local Chrome/profile setup errors, avoid self-loop diagnostics, and emit canonical next actions.
|
|
4
|
-
* Scope: Error presentation only; upstream profile discovery and doctor execution remain normal agent_browser commands.
|
|
5
|
-
*/
|
|
6
1
|
import { isOpenNavigationCommand } from "../../command-taxonomy.js";
|
|
7
2
|
import { getStartupScopedFlags } from "../../runtime.js";
|
|
8
3
|
import { buildNextToolAction } from "../next-actions.js";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Share small presentation formatting and redaction helpers across result presentation modules.
|
|
3
|
-
* Responsibilities: Normalize scalar fields, stringify model-facing values, and apply sensitive-text redaction.
|
|
4
|
-
* Scope: Leaf helpers only; command-family formatting lives in sibling modules.
|
|
5
|
-
*/
|
|
6
1
|
import { containsManagedSessionRestoreKey } from "../../managed-session-capabilities.js";
|
|
7
2
|
import { redactSensitiveText, redactSensitiveValue } from "../../runtime.js";
|
|
8
3
|
import { stringifyUnknown, truncateText } from "../text.js";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Share small ToolPresentation content helpers used by batch and compaction code.
|
|
3
|
-
* Responsibilities: Extract text/image/path fields and format batch step command labels.
|
|
4
|
-
* Scope: Pure ToolPresentation content helpers only.
|
|
5
|
-
*/
|
|
6
1
|
export function isStringArray(value) {
|
|
7
2
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
8
3
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Render diagnostic command families and safe redacted diagnostic data.
|
|
3
|
-
* Responsibilities: Format sessions, profiles, auth/cookies/storage, network diagnostics, console/errors, stream/dashboard/chat, and build network follow-up actions.
|
|
4
|
-
* Scope: Diagnostic/result-state command presentation only; core orchestration stays in presentation.ts.
|
|
5
|
-
*/
|
|
6
1
|
import { isRecord } from "../../parsing.js";
|
|
7
2
|
import { isSensitiveFieldName, redactSensitiveText, redactSensitiveValue } from "../../runtime.js";
|
|
8
3
|
import { classifyNetworkRequestFailure, isApiLikeNetworkRequest, isNetworkArtifactNoiseRequest, summarizeNetworkFailures } from "../network.js";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Compact oversized model-facing tool output into bounded previews and spill artifacts.
|
|
3
|
-
* Responsibilities: Write full output to persistent/session temp storage, update artifact manifests, and preserve safe previews.
|
|
4
|
-
* Scope: Large-output compaction only.
|
|
5
|
-
*/
|
|
6
1
|
import { writePersistentSessionArtifactFile, writeSecureTempFile, } from "../../temp.js";
|
|
7
2
|
import { buildEvictedSessionArtifactEntries } from "../artifact-manifest.js";
|
|
8
3
|
import { countLines, truncateText } from "../text.js";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Remove wrapper-owned managed session and state capabilities from caller-visible list results.
|
|
3
|
-
* Responsibilities: Recognize managed list rows recursively and preserve the surrounding upstream result shape.
|
|
4
|
-
* Scope: Presentation filtering only; command authorization lives in managed-session-state-policy.ts.
|
|
5
|
-
*/
|
|
6
1
|
import { containsManagedSessionRestoreKey, isWrapperManagedSessionName } from "../../managed-session-capabilities.js";
|
|
7
2
|
import { isRecord } from "../../parsing.js";
|
|
8
3
|
function isWrapperManagedSessionListItem(item) {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Format scalar extraction results, navigation summaries, and page-change summaries.
|
|
3
|
-
* Responsibilities: Keep navigation/extraction presentation separate from core tool result orchestration.
|
|
4
|
-
* Scope: Navigation and get/eval extraction formatting only.
|
|
5
|
-
*/
|
|
6
1
|
import { isNavigationObservableCommandName, isPageChangeSummaryCommand } from "../../command-taxonomy.js";
|
|
7
2
|
import { isRecord } from "../../parsing.js";
|
|
8
3
|
import { detectConfirmationRequired } from "../confirmation.js";
|