pi-agent-browser-native 0.2.77 → 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.
Files changed (97) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +8 -17
  3. package/dist/extensions/agent-browser/index.js +127 -19
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +0 -5
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +4 -5
  6. package/dist/extensions/agent-browser/lib/command-policy.js +0 -5
  7. package/dist/extensions/agent-browser/lib/command-taxonomy.js +0 -6
  8. package/dist/extensions/agent-browser/lib/config-policy.js +0 -5
  9. package/dist/extensions/agent-browser/lib/config.js +0 -6
  10. package/dist/extensions/agent-browser/lib/electron/cdp.js +6 -7
  11. package/dist/extensions/agent-browser/lib/electron/cleanup.js +0 -7
  12. package/dist/extensions/agent-browser/lib/electron/discovery.js +0 -7
  13. package/dist/extensions/agent-browser/lib/electron/launch.js +0 -7
  14. package/dist/extensions/agent-browser/lib/input-modes/electron.js +0 -5
  15. package/dist/extensions/agent-browser/lib/input-modes/job.js +0 -5
  16. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +0 -5
  17. package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -6
  18. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +0 -5
  19. package/dist/extensions/agent-browser/lib/input-modes/shared.js +0 -5
  20. package/dist/extensions/agent-browser/lib/input-modes/types.js +0 -5
  21. package/dist/extensions/agent-browser/lib/json-schema.js +0 -5
  22. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +4 -5
  23. package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
  24. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +0 -3
  25. package/dist/extensions/agent-browser/lib/managed-session-restore.js +30 -13
  26. package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +0 -5
  27. package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +1 -7
  28. package/dist/extensions/agent-browser/lib/managed-session-storage.js +0 -5
  29. package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +41 -30
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +17 -11
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +22 -5
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +35 -7
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +17 -1
  40. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +35 -18
  41. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +4 -1
  42. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
  43. package/dist/extensions/agent-browser/lib/parsing.js +0 -7
  44. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +4 -1
  45. package/dist/extensions/agent-browser/lib/playbook.js +3 -10
  46. package/dist/extensions/agent-browser/lib/process-identity.js +0 -5
  47. package/dist/extensions/agent-browser/lib/process.js +0 -7
  48. package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
  49. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -8
  50. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +6 -7
  51. package/dist/extensions/agent-browser/lib/results/categories.js +1 -8
  52. package/dist/extensions/agent-browser/lib/results/confirmation.js +0 -7
  53. package/dist/extensions/agent-browser/lib/results/contracts.js +0 -7
  54. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +0 -7
  55. package/dist/extensions/agent-browser/lib/results/envelope.js +0 -7
  56. package/dist/extensions/agent-browser/lib/results/network.js +0 -7
  57. package/dist/extensions/agent-browser/lib/results/next-actions.js +0 -30
  58. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +1 -7
  59. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
  60. package/dist/extensions/agent-browser/lib/results/presentation/common.js +0 -5
  61. package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
  62. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +0 -5
  63. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +0 -5
  64. package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -5
  65. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +0 -5
  66. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +0 -5
  67. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
  68. package/dist/extensions/agent-browser/lib/results/presentation.js +0 -5
  69. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +0 -7
  70. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
  71. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +0 -7
  72. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +0 -7
  73. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +0 -7
  74. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +0 -7
  75. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +0 -7
  76. package/dist/extensions/agent-browser/lib/results/snapshot.js +0 -7
  77. package/dist/extensions/agent-browser/lib/results/text.js +0 -7
  78. package/dist/extensions/agent-browser/lib/runtime.js +4 -14
  79. package/dist/extensions/agent-browser/lib/session-page-state.js +0 -7
  80. package/dist/extensions/agent-browser/lib/string-enum-schema.js +0 -5
  81. package/dist/extensions/agent-browser/lib/temp.js +0 -7
  82. package/dist/extensions/agent-browser/lib/web-search.js +0 -5
  83. package/docs/ARCHITECTURE.md +16 -16
  84. package/docs/COMMAND_REFERENCE.md +8 -7
  85. package/docs/ELECTRON.md +3 -19
  86. package/docs/RELEASE.md +5 -17
  87. package/docs/REQUIREMENTS.md +4 -4
  88. package/docs/SUPPORT_MATRIX.md +17 -17
  89. package/docs/TOOL_CONTRACT.md +18 -16
  90. package/docs/platform-smoke.md +1 -1
  91. package/package.json +5 -6
  92. package/scripts/doctor.mjs +1 -1
  93. package/dist/extensions/agent-browser/lib/electron/text.js +0 -13
  94. package/dist/extensions/agent-browser/lib/input-modes.js +0 -10
  95. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +0 -5
  96. package/dist/extensions/agent-browser/lib/results/artifact-state.js +0 -13
  97. package/dist/extensions/agent-browser/lib/results.js +0 -14
@@ -1,16 +1,7 @@
1
- /**
2
- * Purpose: Build safe, deterministic agent-browser invocations and persisted session state for the pi-agent-browser extension.
3
- * Responsibilities: Validate raw tool arguments, derive extension-managed session names from the pi session identity, restore managed-session state from persisted tool details, redact sensitive invocation text, classify browser-oriented prompts, and build the effective CLI argument list passed to the upstream agent-browser binary.
4
- * Scope: Runtime-planning helpers only; no subprocess execution or filesystem access.
5
- * Usage: Imported by the extension entrypoint and unit tests before spawning the upstream CLI.
6
- * Invariants/Assumptions: The wrapper stays thin, preserves upstream command vocabulary, keeps plain-text inspection stateless,
7
- * and only injects wrapper-owned flags: `--json`, an extension-managed `--session` when appropriate, the narrow
8
- * site-specific headless compatibility `--user-agent` when that workaround applies.
9
- */
10
1
  import { createHash, randomUUID } from "node:crypto";
11
2
  import { basename } from "node:path";
12
3
  import { findCommandStartIndex, parseArgvDescriptor, parseCommandInfo, } from "./argv-descriptor.js";
13
- import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, getBooleanFlagValue, GLOBAL_VALUE_FLAGS_ALLOWING_DASH_VALUE, PREVALIDATED_VALUE_FLAGS, resolveAgentBrowserNamespace, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
4
+ import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, getBooleanFlagValue, GLOBAL_VALUE_FLAGS_ALLOWING_DASH_VALUE, isUpstreamEnvFlagEnabled, PREVALIDATED_VALUE_FLAGS, resolveAgentBrowserNamespace, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
14
5
  import { needsManagedSession } from "./command-policy.js";
15
6
  import { isWrapperManagedSessionName, redactManagedSessionRestoreKeys } from "./managed-session-capabilities.js";
16
7
  import { isCloseCommand, isOpenNavigationCommand } from "./command-taxonomy.js";
@@ -368,7 +359,7 @@ export function resolveManagedSessionState(options) {
368
359
  sessionName: managedSessionName,
369
360
  };
370
361
  }
371
- function isRestorableManagedSessionName(sessionName, fallbackSessionName) {
362
+ export function isRestorableManagedSessionName(sessionName, fallbackSessionName) {
372
363
  return sessionName === fallbackSessionName || sessionName.startsWith(`${fallbackSessionName}-fresh-`);
373
364
  }
374
365
  function getManagedSessionRestoreRank(options) {
@@ -699,9 +690,8 @@ export function canUseHeadlessCompatibilityUserAgent(args, env = process.env) {
699
690
  return false;
700
691
  if (env.AGENT_BROWSER_USER_AGENT !== undefined || env.AGENT_BROWSER_ARGS !== undefined || env.AGENT_BROWSER_CDP !== undefined || env.AGENT_BROWSER_PROVIDER !== undefined)
701
692
  return false;
702
- const envFlagEnabled = (value) => value !== undefined && !["", "0", "false", "no"].includes(value.toLowerCase());
703
- if ((getBooleanFlagValue(args, "--headed") ?? envFlagEnabled(env.AGENT_BROWSER_HEADED))
704
- || (getBooleanFlagValue(args, "--auto-connect") ?? envFlagEnabled(env.AGENT_BROWSER_AUTO_CONNECT)))
693
+ if ((getBooleanFlagValue(args, "--headed") ?? isUpstreamEnvFlagEnabled(env.AGENT_BROWSER_HEADED))
694
+ || (getBooleanFlagValue(args, "--auto-connect") ?? isUpstreamEnvFlagEnabled(env.AGENT_BROWSER_AUTO_CONNECT)))
705
695
  return false;
706
696
  const engine = scanUpstreamGlobalFlagOccurrences(args, "--engine").at(-1)?.value ?? env.AGENT_BROWSER_ENGINE;
707
697
  return !engine || engine === "chrome";
@@ -1,10 +1,3 @@
1
- /**
2
- * Purpose: Own wrapper-known per-session browser page target, ref snapshot, invalidation, and pinning state.
3
- * Responsibilities: Restore state from persisted tool details, apply ordered tab/ref updates atomically, and expose order-free public state views to the extension entrypoint.
4
- * Scope: Session page state only; browser process execution, tab probing, and presentation policies stay in the extension entrypoint.
5
- * Usage: `index.ts` creates one store per Pi session lifecycle and records observations through update tokens.
6
- * Invariants/Assumptions: One tool-call update token must govern all page-state observations from that invocation; stale overlapping updates must not overwrite newer state.
7
- */
8
1
  import { getAgentBrowserSessionIdentityKey } from "./argv-grammar.js";
9
2
  import { isCloseCommand, isReadOnlyDiagnosticSessionTargetCommand, isUnverifiedPageTransitionCommand } from "./command-taxonomy.js";
10
3
  import { isRecord } from "./parsing.js";
@@ -1,8 +1,3 @@
1
- /**
2
- * Purpose: Build compact JSON-schema string enums without importing pi runtime helpers.
3
- * Responsibilities: Mirror pi-ai StringEnum's `{ type: "string", enum: [...] }` shape while keeping extension startup imports light.
4
- * Scope: Schema construction only.
5
- */
6
1
  import { JsonSchema } from "./json-schema.js";
7
2
  export function StringEnum(values, options) {
8
3
  return JsonSchema.Unsafe({
@@ -1,10 +1,3 @@
1
- /**
2
- * Purpose: Create private temporary and persisted spill files for the pi-agent-browser extension without leaking artifacts broadly on disk.
3
- * Responsibilities: Maintain a process-private temp root, stamp explicit ownership/protected-child markers, enforce an aggregate temp-artifact disk budget, create securely permissioned temp files, create session-scoped persisted spill files for resumable sessions, prune explicitly owned stale temp roots from prior runs without deleting protected children, and best-effort clean all owned roots on process exit.
4
- * Scope: Artifact lifecycle helpers only; callers decide what data to write and when to delete or retain long-lived references.
5
- * Usage: Imported by result/process helpers when they need secure spill files instead of world-readable shared tmp paths.
6
- * Invariants/Assumptions: Temp artifacts live under the OS temp directory, each active run uses a dedicated 0700 directory, files are created with exclusive 0600 permissions, session-scoped persisted artifacts stay under the pi session directory, and stale pruning only touches roots with an explicit pi-agent-browser ownership marker.
7
- */
8
1
  import { randomBytes } from "node:crypto";
9
2
  import { existsSync, readdirSync, rmSync } from "node:fs";
10
3
  import { chmod, mkdir, mkdtemp, open, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
@@ -1,8 +1,3 @@
1
- /**
2
- * Purpose: Provide the optional provider-backed `agent_browser_web_search` companion tool.
3
- * Responsibilities: Define strict search input schema, resolve configured Brave/Exa credentials lazily, call the selected search API with cancellation/timeout, normalize compact results, and keep secrets out of content/details.
4
- * Scope: Live web search only; browser automation remains in the `agent_browser` tool.
5
- */
6
1
  import { JsonSchema } from "./json-schema.js";
7
2
  import { WEB_SEARCH_PROMPT_GUIDELINE } from "./playbook.js";
8
3
  import { StringEnum as localStringEnum } from "./string-enum-schema.js";
@@ -40,7 +40,7 @@ The extension should:
40
40
  - inject `--json`
41
41
  - complete each upstream invocation when the direct `agent-browser` child exits even if Node delays `"close"`: piped stdio can stay referenced by longer-lived descendant processes, so `runAgentBrowserProcess` watches `exit` and `close` together, leaves stdio intact during a short post-`exit` grace so normal `close` can still win, destroys streams only when the post-`exit` fallback fires, and prefers `close` codes then wrapper timeout (`124`) over signal-shaped `exit` codes (`watchSpawnedChildCompletion` / `resolveSpawnedChildExitCode` in `extensions/agent-browser/lib/process.ts`) so the tool cannot hang after the CLI process has already terminated
42
42
  - support optional stdin only for `eval --stdin`, `batch`, `auth save --password-stdin`, and wrapper-generated `batch` stdin from top-level `job`, `qa`, `sourceLookup`, or `networkSourceLookup`, rejecting other command/stdin combinations before launch; top-level `electron` never accepts caller `stdin` (see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#electron))
43
- - support optional top-level `outputPath` for successful browser results by writing `details.data` (or model-facing text when no structured data exists) to a caller-requested local file and reporting `details.outputFile`, without changing upstream argv semantics
43
+ - support optional top-level `outputPath` for successful browser results by writing `details.data` (or model-facing text when no structured data exists) to a caller-requested local file and reporting `details.outputFile`, without changing upstream argv semantics or overwriting a browser artifact when both destinations resolve to the same file
44
44
  - support optional top-level `timeoutMs` as a per-call subprocess watchdog override for browser CLI input modes while keeping Electron-specific timeouts inside the `electron` object
45
45
  - accept an optional native `semanticAction` object as a mutually exclusive alternative to `args` on a single tool call (and to `job`, `qa`, `sourceLookup`, `networkSourceLookup`, and `electron` on the same call), compile locator actions into upstream `find` argv, direct selector/ref click/check/fill into upstream command argv, and native dropdown selection into upstream `select <selector> <value...>` argv (with optional `semanticAction.session` expanding to a leading `--session <name>` before the compiled command when targeting a named upstream browser instead of the managed default), and echo the compiled shape in `details.compiledSemanticAction` for observability (see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#semanticaction))
46
46
  - accept an optional native `job` object (mutually exclusive with `args`, `semanticAction`, `qa`, `sourceLookup`, `networkSourceLookup`, and `electron` on the same call) with a small fixed step vocabulary that compiles only to existing upstream `batch` argv rows, generates the JSON batch stdin string internally, defaults to fail-fast `batch --bail` unless `failFast:false` is explicit, supports semantic locator fields on constrained `click`/`fill` steps by reusing the top-level semantic-action compiler, supports human-paced `type` by expanding to a bounded set of existing focus/keyboard/wait/press rows while compacting model-visible batch text, and echoes `details.compiledJob` for observability (see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#job))
@@ -68,7 +68,7 @@ Pi docs use `settings.json` for package/resource loading and filtering, not arbi
68
68
  - project-local: `.pi/config/pi-agent-browser-native/config.json`
69
69
  - explicit override: `PI_AGENT_BROWSER_CONFIG=/path/to/config.json`
70
70
 
71
- Config layers merge in that order: global, project, override. The shared policy module (`extensions/agent-browser/lib/config-policy.js`) owns provider descriptors, environment variable names, config keys, credential source parsing, developer-trusted project layer inclusion, layer validation/merge, redacted status projection, and credential summaries for both runtime config loading and the package config helper. Under Pi 0.79+, globally installed or CLI-loaded extensions are developer-trusted code, so this extension reads `.pi/config/pi-agent-browser-native/config.json` by default and skips that project layer when Pi reports the project is untrusted or when launched with `--no-approve`. Global config and explicit `PI_AGENT_BROWSER_CONFIG` overrides remain available either way. The config reader accepts v1 fields for `webSearch.enabled`, `webSearch.preferredProvider`, `webSearch.exaApiKey`, `webSearch.braveApiKey`, and conservative browser defaults such as `browser.defaultProfile` and `browser.executablePath`. Web-search key fields follow Pi model/provider-style value resolution from any loaded layer: literal values, `$ENV_VAR` / `${ENV_VAR}` interpolation, escapes (`$$`, `$!`), and leading `!command` resolved at request time. `EXA_API_KEY` and `BRAVE_API_KEY` remain environment fallbacks when no config credential source exists for that provider. Browser default values keep their source scope; prompt guidance is emitted from the highest-priority loaded layer, including project config when Pi trust/loading allows it.
71
+ Config layers merge in that order: global, project, override. The shared policy module (`extensions/agent-browser/lib/config-policy.js`) owns provider descriptors, environment variable names, config keys, credential source parsing, developer-trusted project layer inclusion, layer validation/merge, redacted status projection, and credential summaries for both runtime config loading and the package config helper. Under Pi 0.84.0+, globally installed or CLI-loaded extensions are developer-trusted code, so this extension reads `.pi/config/pi-agent-browser-native/config.json` by default and skips that project layer when Pi reports the project is untrusted or when launched with `--no-approve`. Global config and explicit `PI_AGENT_BROWSER_CONFIG` overrides remain available either way. The config reader accepts v1 fields for `webSearch.enabled`, `webSearch.preferredProvider`, `webSearch.exaApiKey`, `webSearch.braveApiKey`, and conservative browser defaults such as `browser.defaultProfile` and `browser.executablePath`. Web-search key fields follow Pi model/provider-style value resolution from any loaded layer: literal values, `$ENV_VAR` / `${ENV_VAR}` interpolation, escapes (`$$`, `$!`), and leading `!command` resolved at request time. `EXA_API_KEY` and `BRAVE_API_KEY` remain environment fallbacks when no config credential source exists for that provider. Browser default values keep their source scope; prompt guidance is emitted from the highest-priority loaded layer, including project config when Pi trust/loading allows it.
72
72
 
73
73
  `agent_browser_web_search` availability is conditional. Startup registration uses global, override, and environment fallback config without reading project-local config before Pi trust context exists; trusted project config can register the companion tool on `session_start`, and every execution reloads the final session config so `webSearch.enabled: false` still prevents a request even if a startup credential made the tool visible. A global disable is the normal user default and can still be overridden by project config or `PI_AGENT_BROWSER_CONFIG`; a project disable applies to one repo; an explicit `PI_AGENT_BROWSER_CONFIG` file with `webSearch.enabled: false` is the highest-priority hard disable for that run. Literal and env-backed sources must resolve before they make the tool available; command-backed sources are considered configured without running the command until tool execution, so secret managers do not slow startup or prompt unexpectedly. The tool resolves the selected key lazily, chooses Exa or Brave from available credentials (preferring Exa by default unless `webSearch.preferredProvider` says otherwise), then follows one provider-agnostic execution path through provider adapters for request building, HTTP JSON fetch, response normalization, and provider-specific detail fields. It calls Exa `/search` with highlights or Brave Search and returns compact result details without exposing keys.
74
74
 
@@ -85,12 +85,12 @@ Tier B guidance lives in `SHARED_BROWSER_PLAYBOOK_GUIDELINES`, generated README/
85
85
  Do **not** add reusable browser recipes as a first-class runtime surface yet.
86
86
 
87
87
  Current evidence does not justify another source of truth for workflows:
88
- - the deterministic efficiency benchmark in [`scripts/agent-browser-efficiency-benchmark.mjs`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/agent-browser-efficiency-benchmark.mjs) models one native `job` scenario (`job-open-assert-screenshot`), one `qa` preset (`qa-open-diagnostics`), one `sourceLookup` (`source-lookup-visible-element`), one `networkSourceLookup` (`network-source-lookup-failed-request`), plus deterministic `electron` lifecycle/probe scenarios (`electron-lifecycle`, `electron-probe`) rather than repeated named job patterns that agents keep re-specifying
88
+ - unit/fake-upstream coverage and real-app dogfood exercise `job`, `qa`, experimental lookups, and `electron` lifecycle without a separate accounting benchmark layer
89
89
  - repo-local dogfood evidence does not show repeated project-specific job recipes that need versioning or ownership
90
90
  - `qa` already covers the only repeated smoke-test shape with a stable top-level preset
91
91
  - docs and prompt guidance can carry examples without adding recipe state, migration rules, or another schema
92
92
 
93
- Revisit this only when benchmark or dogfood data shows at least two repeated, failure-prone job sequences that cannot be represented clearly by `job`, `qa`, top-level `electron`, or raw `batch`. If that happens, define ownership, versioning, schema boundaries, generated docs, and tests before adding executable recipes.
93
+ Revisit this only when dogfood data shows at least two repeated, failure-prone job sequences that cannot be represented clearly by `job`, `qa`, top-level `electron`, or raw `batch`. If that happens, define ownership, versioning, schema boundaries, generated docs, and tests before adding executable recipes.
94
94
 
95
95
  ### Package layout versus local checkout development
96
96
 
@@ -99,7 +99,7 @@ The published package should load from the `pi` manifest in `package.json`.
99
99
  Local checkout validation has two intentional modes:
100
100
 
101
101
  - **Quick isolated mode:** use explicit CLI loading such as `pi --approve --no-extensions -e .` from the repository root when this checkout is intentionally trusted. This bypasses Pi settings and extension discovery, avoids duplicate `agent_browser` registrations when another source is installed globally, and is the right mode for checkout smoke tests; omit `--approve` only when deliberately testing Pi's Project Trust prompt.
102
- - **Configured-source lifecycle mode:** configure exactly one active checkout or package source in Pi settings and launch plain `pi` for manual validation, or run the automated harness that launches with `--approve`. This is the right mode for validating `/reload` and exact-session relaunch because those lifecycle checks exercise discovered/configured resources. Focused extension harness tests validate branch-backed `session_tree` rehydration and cleanup ownership. Before shipping, maintainers also run `npm run verify -- lifecycle` (same semantics under automation, using Pi 0.79 `--approve --session-id` to reopen the exact JSONL session) plus the live-site checks in [`RELEASE.md`](RELEASE.md#pre-release-checks); `npm publish` enforces `npm run verify -- release` via `prepublishOnly` unless scripts are skipped.
102
+ - **Configured-source lifecycle mode:** configure exactly one active checkout or package source in Pi settings and launch plain `pi` for manual validation, or run the automated harness that launches with `--approve`. This is the right mode for validating `/reload` and exact-session relaunch because those lifecycle checks exercise discovered/configured resources. Focused extension harness tests validate branch-backed `session_tree` rehydration and cleanup ownership. Before shipping, maintainers also run `npm run verify -- lifecycle` (same semantics under automation, using Pi 0.84.0+ `--approve --session-id` to reopen the exact JSONL session) plus the live-site checks in [`RELEASE.md`](RELEASE.md#pre-release-checks); `npm publish` enforces `npm run verify -- release` via `prepublishOnly` unless scripts are skipped.
103
103
 
104
104
  The repo should not add a repo-local `.pi/extensions/` autoload shim as the documented checkout path.
105
105
 
@@ -109,7 +109,7 @@ Why:
109
109
  - keeps reload and exact-session relaunch validation tied to Pi's configured-source lifecycle instead of an isolated quick-test path, while `session_tree` state changes stay covered by focused extension harness tests
110
110
  - keeps the published tarball focused on the package manifest, extension code, canonical docs, and license
111
111
 
112
- The published package should exclude agent-only and internal planning materials such as `AGENTS.md` and `docs/plans/`.
112
+ The published package should exclude agent-only and internal planning materials such as `AGENTS.md`.
113
113
 
114
114
  ## Session model
115
115
 
@@ -137,19 +137,19 @@ V1 ownership rule:
137
137
  - extension-managed sessions should be reusable during an active `pi` session and across `/reload`, exact-session relaunch, `/resume`, and Pi branch-tree transitions, while still being cleaned up predictably
138
138
 
139
139
  Practical policy:
140
- - preserve the current branch-visible extension-managed session across `/reload`, exact-session relaunch, `/resume`, and Pi 0.79 `session_tree` branch transitions so persisted sessions can keep following the live browser after lifecycle changes
140
+ - preserve the current branch-visible extension-managed session across `/reload`, exact-session relaunch, `/resume`, and Pi 0.84.0+ `session_tree` branch transitions so persisted sessions can keep following the live browser after lifecycle changes
141
141
  - close the active extension-managed session when the originating `pi` process quits, while leaving explicit caller-provided sessions alone
142
142
  - set an idle timeout on extension-managed sessions as a backstop for abnormal exits or cleanup failures, and apply that same `AGENT_BROWSER_IDLE_TIMEOUT_MS` value to every upstream subprocess (including wrapper helper snapshots, tab lists, and navigation-summary reads) because changing the launch environment between calls can make upstream restart the background browser, discard the active tab, and invalidate fresh refs
143
- - for wrapper-owned managed sessions only, also set a Git-checkout-generation-stable `AGENT_BROWSER_RESTORE` key on every compatible non-close upstream subprocess so cookies, localStorage, and sessionStorage autosave/restore across idle shutdowns and later Pi chats in the same checkout generation. The wrapper stores a UUID in the resolved Git admin directory and combines it with the checkout root and Git-admin directory filesystem identities: renames preserve the key, copied or path-replacement checkouts get a new key, non-Git directories fail closed, and cwd-only keys are not adopted. Policy lives in `extensions/agent-browser/lib/managed-session-restore.ts`; ownership is resolved by `resolveOwnedManagedSessionContext` (injected managed session, or explicit `--session` equal to the current managed name and namespace) and applied through `AsyncLocalStorage` `withOwnedManagedSessionContext` for prepare helpers plus main process/output, with typed `ownedManagedSession` process options for owned main/close spawns rather than an internal marker leaked into the child environment. `buildOwnedManagedSessionRestoreContext` sets call-scoped `restoreSuppressed` from main-plan argv so helper probes skip restore on incompatible plans without sticky-disabling when prepare returns early; sticky disable commits only after an owned-context subprocess actually starts with suppressed restore policy: POSIX commits on child `spawn`, while PowerShell-backed Windows commits after completion unless command-not-found stderr proves `agent-browser.cmd` never started. No-spawn preflight and missing-binary failures never commit an identity. Duplicate `--session` or `--namespace` flags are rejected, as are leading equals forms that upstream 0.33.2 does not recognize; global identity/config scanning follows upstream across the full argv rather than treating `--` as a sentinel. Native Windows command-first launcher adaptation relocates only valid leading global syntax, canonicalizes a valued optional `--restore <name>` to `--restore=<name>`, consumes only exact lowercase boolean literals, and leaves command-scoped, unknown, or unsupported equals-form input untouched so invalid calls cannot become valid browser activity. Namespace values are canonicalized with upstream's lowercase `sanitize_session_component` algorithm before ownership, sticky/page state, details, socket, or restore-directory identity comparisons; every wrapper-owned subprocess also pins that canonical namespace, including an empty default namespace, so parent environment cannot redirect helpers or close. Electron status target reads and current-managed probes acquire the same daemon-policy lock and owned restore context as ordinary commands for their underlying reads. Probe results then persist the same namespace plus top-level tab/ref state, keeping branch replay keyed to the probed identity. Ownership is typed rather than inferred from a name prefix, and `piab-*` live-session names are reserved: an explicit target is accepted only when it is the current/generated managed session or appears in this extension instance's ownership records. `session list` hides those rows, and the same reservation is rechecked at the final process boundary so another Pi process cannot attach to a managed authenticated browser through the shared per-user daemon socket. Skip when the caller already set restore/profile/state/CDP/provider/auto-connect/containment/session-name or a browser mutation surface (custom executable, extension, init script, raw launch args, proxy, plugin, WebGPU, or related engine/device controls) via argv or matching parent env, when the command is `connect`, when raw batch argv is used, when batch stdin contains nested `connect`/`batch`, or when `PI_AGENT_BROWSER_MANAGED_SESSION_RESTORE=0`. The wrapper's own site-specific headless user-agent compatibility injection is excluded from caller-mutation policy and remains pinned across follow-up subprocesses for the active managed session. For those owned sessions only, the process boundary also replaces the otherwise-empty safe `--args` value with a fixed, comma-safe Chrome `--user-agent=...` launch argument so new targets inherit the compatibility value; upstream's normal `--user-agent` flag applies only to the active CDP target. A user-private immutable ticket-claim lock keyed by canonical namespace/session serializes this inspect-through-spawn decision across cooperating Pi processes; every contender publishes a unique claim, deterministic tickets elect one owner, and the winner also holds the legacy v2 path as a bridge. The bridge is transitional for pre-release branch processes and is scheduled for removal after v0.2.74 in [#93](https://github.com/fitchmultz/pi-agent-browser-native/issues/93). Live pre-update processes and their in-flight candidate gaps therefore block new acquisition; an abandoned v2 owner fails closed for manual repair, while current-protocol recovery removes only unique claims and artifacts with proven-dead PID/start identity. Waits are asynchronous and bounded. Every policy-lock winner re-runs `session info` even when this process previously recorded the applied/observed restore key, because another process can restart the same daemon identity between calls. That inspection uses a fixed bounded timeout independent of a caller's shorter `PI_AGENT_BROWSER_PROCESS_TIMEOUT_MS` override. Before an incompatible call, the wrapper reads `session info` for the actual same-identity daemon and fails before the requested spawn when that daemon retains any restore key or cannot be inspected. This covers restore-enabled daemons missing from transcript state after a crash and managed sessions launched with an explicit caller restore key; a confirmed inactive daemon remains reusable; a restore-disabled daemon is reusable only when this process recorded its expected null/custom restore policy after an owned spawn or successful policy match. After reload clears process-only provenance, an inactive old daemon may be restarted without restore and that started subprocess records a null daemon policy for its next follow-up. Same-process `session_tree` branch changes retain that process-owned provenance; a new extension instance after reload, restart, or `/resume` intentionally starts without it and fails closed on a still-live restore-disabled daemon even when the transcript restores sticky-disable state. Close the retained-key daemon first, use a fresh wrapper session, or choose a distinct explicit session. Once a managed session hits any allowed incompatible launch path, restore stays disabled for later bare follow-ups on that same session identity. Sticky identities live in the extension-owned `ManagedSessionRestoreState` instance, persist as `details.managedSessionRestoreDisabled`, and are replaced from current-branch rows during branch restore rather than stored in module-global process state. The opt-out returns before config/storage probes and sticky-records a successfully spawned identity as restore-disabled, allowing later calls to reuse that non-restore daemon without tripping the active restore-enabled conflict gate. This is env-based persistence, not a hidden argv relaunch. Upstream still owns restore file paths/modes under `~/.agent-browser/`; set `AGENT_BROWSER_ENCRYPTION_KEY` on multi-user hosts if plaintext session files are unacceptable. Any project `./agent-browser.json`, explicit `--config` / `AGENT_BROWSER_CONFIG`, or `~/.agent-browser/config.json` discovered while planning disables managed restore without reading caller-selected content in the Pi host; owned spawns sticky-disable that session identity. Each subprocess that receives the wrapper restore key, plus every wrapper-owned close, overrides config discovery with a process-private empty `AGENT_BROWSER_CONFIG` (`0400` on POSIX) inside the canonical marked `0700` secure-temp root, closing the check-to-spawn race without trusting project or user config while retaining normal shutdown cleanup and PID/start-identity stale-root recovery after abnormal exit on POSIX and native Windows; versioned Windows identities treat legacy cross-format markers as unknown instead of incorrectly proving PID reuse, and temp ownership marker schema v2 makes older readers ignore new-format markers. Spawn-time revalidation rejects changed checkout identity, restore storage, unpinned launch-mutator environment, foreign managed-session targets, or forbidden managed-state access before agent-browser starts; the same check runs again after protected-config and socket-directory awaits immediately adjacent to the synchronous spawn. A failed fresh command that started agent-browser triggers an exact-identity daemon probe; an active or uninspectable daemon remains current and wrapper-owned so shutdown cleanup can close it, while pre-aborted and missing-binary calls remain unowned. Wrapper-owned close commands canonicalize upstream argv to JSON plus the known namespace/session and `close`, discarding caller config/restore globals, and do not inject a newly derived restore key into an existing daemon, so checkout replacement cannot make old auth save under the replacement generation; the close path retains the observed wrapper key long enough to record the returned old-generation snapshot safely. Because upstream writes a snapshot per daemon session, a successful wrapper-owned close requests JSON output and persists only the returned state path as an atomic record in a lockless convergent per-key ownership directory beside the snapshots (`0700`, with `0600` records, on POSIX). Cleanup carries that ownership proof across Pi restarts, self-heals malformed or stale regular records without claiming their snapshots, uses immutable atomic record names plus rescan-after-delete convergence so concurrent closers cannot skip ownership recording or exceed the aggregate cap, removes proven snapshots older than 30 days for the exact restore key while retaining the two newest, expires stale ownership-proven snapshots and empty manifests from other restore-key generations after 30 days only when a private lineage record proves the same canonical checkout path, caps young close churn at 256 records per key, and never deletes matching unrecorded files or the current checkout key. Upstream restore files under `~/.agent-browser/` remain plaintext unless `AGENT_BROWSER_ENCRYPTION_KEY` is set; before automatic managed restore the wrapper requires a durable Git generation and absolute platform home root; it pins the planned encryption-key value after caller env merging; on POSIX it also resolves `HOME` once, validates owner-trusted non-writable ancestry plus stable device/inode/birth-time metadata for both checkout and Git-admin directories, and pins that canonical value, enforces mode `0700` without silently repairing unsafe existing paths, and rejects symlinks/non-directories along the exact `~/.agent-browser[/namespaces/<canonical>/state]/sessions` path and its `.tmp` transactional-write area, while Windows requires an absolute `USERPROFILE` and the documented 64-character hex encryption key because POSIX mode checks cannot verify profile ACLs; malformed keys fail closed on every platform. POSIX process-start probes use absolute `/bin/ps` then `/usr/bin/ps`; if neither is available, managed policy locking fails closed with an actionable validation message. Managed `piab-r2-*` keys and key-bearing paths are redacted from visible/structured/JSON transcript surfaces. Malformed oversized upstream output is discarded after parsing rather than copied into a persistent parse-failure spill, and raw parse-failure stdout is omitted from result details. `session list` and `state list` filter wrapper-managed rows, and the pre-spawn policy blocks foreign managed restore/state references, broad clear/clean operations, and managed save/rename targets while preserving targeted caller-owned state workflows.
143
+ - for wrapper-owned managed sessions only, also set a Git-checkout-generation-stable `AGENT_BROWSER_RESTORE` key on every compatible non-close upstream subprocess so cookies, localStorage, and sessionStorage autosave/restore across idle shutdowns and later Pi chats in the same checkout generation. The wrapper stores a UUID in the resolved Git admin directory and combines it with the checkout root and Git-admin directory filesystem identities: renames preserve the key, copied or path-replacement checkouts get a new key, non-Git directories fail closed, and cwd-only keys are not adopted. Policy lives in `extensions/agent-browser/lib/managed-session-restore.ts`; ownership is resolved by `resolveOwnedManagedSessionContext` (injected managed session, or explicit `--session` equal to the current managed name and namespace) and applied through `AsyncLocalStorage` `withOwnedManagedSessionContext` for prepare helpers plus main process/output, with typed `ownedManagedSession` process options for owned main/close spawns rather than an internal marker leaked into the child environment. `buildOwnedManagedSessionRestoreContext` sets call-scoped `restoreSuppressed` from main-plan argv so helper probes skip restore on incompatible plans without sticky-disabling when prepare returns early; sticky disable commits only after an owned-context subprocess actually starts with suppressed restore policy: POSIX commits on child `spawn`, while PowerShell-backed Windows commits after completion unless command-not-found stderr proves `agent-browser.cmd` never started. No-spawn preflight and missing-binary failures never commit an identity. Duplicate `--session` or `--namespace` flags are rejected, as are leading equals forms that upstream 0.33.2 does not recognize; global identity/config scanning follows upstream across the full argv rather than treating `--` as a sentinel. Native Windows command-first launcher adaptation relocates only valid leading global syntax, canonicalizes a valued optional `--restore <name>` to `--restore=<name>`, consumes only exact lowercase boolean literals, and leaves command-scoped, unknown, or unsupported equals-form input untouched so invalid calls cannot become valid browser activity. Namespace values are canonicalized with upstream's lowercase `sanitize_session_component` algorithm before ownership, sticky/page state, details, socket, or restore-directory identity comparisons; every wrapper-owned subprocess also pins that canonical namespace, including an empty default namespace, so parent environment cannot redirect helpers or close. Electron status target reads and current-managed probes acquire the same daemon-policy lock and owned restore context as ordinary commands for their underlying reads. Probe results then persist the same namespace plus top-level tab/ref state, keeping branch replay keyed to the probed identity. Ownership is typed rather than inferred from a name prefix, and `piab-*` live-session names are reserved: an explicit target is accepted only when it is the current/generated managed session or appears in this extension instance's ownership records. `session list` hides those rows, and the same reservation is rechecked at the final process boundary so another Pi process cannot attach to a managed authenticated browser through the shared per-user daemon socket. Skip when the caller already set restore/profile/state/CDP/provider/auto-connect/containment/session-name or a browser mutation surface (custom executable, extension, init script, raw launch args, proxy, plugin, WebGPU, or related engine/device controls) via argv or matching parent env, when the command is `connect`, when raw batch argv is used, when batch stdin contains nested `connect`/`batch`, or when `PI_AGENT_BROWSER_MANAGED_SESSION_RESTORE=0`. The wrapper's own site-specific headless user-agent compatibility injection is excluded from caller-mutation policy and remains pinned across follow-up subprocesses for the active managed session. For those owned sessions only, the process boundary also replaces the otherwise-empty safe `--args` value with a fixed, comma-safe Chrome `--user-agent=...` launch argument so new targets inherit the compatibility value; upstream's normal `--user-agent` flag applies only to the active CDP target. A user-private immutable ticket-claim lock keyed by canonical namespace/session serializes this inspect-through-spawn decision across cooperating Pi processes; every contender publishes a unique claim, deterministic tickets elect one owner, and the winner also holds the legacy v2 path as a bridge. The bridge is transitional for pre-release branch processes and is scheduled for removal after v0.2.74 in [#93](https://github.com/fitchmultz/pi-agent-browser-native/issues/93). Live pre-update processes and their in-flight candidate gaps therefore block new acquisition; an abandoned v2 owner fails closed for manual repair, while current-protocol recovery removes only unique claims and artifacts with proven-dead PID/start identity. Waits are asynchronous and bounded. Ordinary acquisition waits one second and fails with retry guidance rather than queueing behind another process's in-flight command, which may retain the lock through a 35-second daemon inspection and the requested browser operation. Every policy-lock winner re-runs `session info` even when this process previously recorded the applied/observed restore key, because another process can restart the same daemon identity between calls. That inspection uses a fixed bounded timeout independent of a caller's shorter `PI_AGENT_BROWSER_PROCESS_TIMEOUT_MS` override. Before an incompatible call, the wrapper reads `session info` for the actual same-identity daemon and fails before the requested spawn when that daemon retains any restore key or cannot be inspected. This covers restore-enabled daemons missing from transcript state after a crash and managed sessions launched with an explicit caller restore key; a confirmed inactive daemon remains reusable; a restore-disabled daemon is reusable only when this process recorded its expected null/custom restore policy after an owned spawn or successful policy match. After reload clears process-only provenance, an inactive old daemon may be restarted without restore and that started subprocess records a null daemon policy for its next follow-up. Same-process `session_tree` branch changes retain that process-owned provenance; a new extension instance after reload, restart, or `/resume` intentionally starts without it and fails closed on a still-live restore-disabled daemon even when the transcript restores sticky-disable state. Close the retained-key daemon first, use a fresh wrapper session, or choose a distinct explicit session. Once a managed session hits any allowed incompatible launch path, restore stays disabled for later bare follow-ups on that same session identity. Sticky identities live in the extension-owned `ManagedSessionRestoreState` instance, persist as `details.managedSessionRestoreDisabled`, and are replaced from current-branch rows during branch restore rather than stored in module-global process state. The opt-out returns before config/storage probes and sticky-records a successfully spawned identity as restore-disabled, allowing later calls to reuse that non-restore daemon without tripping the active restore-enabled conflict gate. This is env-based persistence, not a hidden argv relaunch. Upstream still owns restore file paths/modes under `~/.agent-browser/`; set `AGENT_BROWSER_ENCRYPTION_KEY` on multi-user hosts if plaintext session files are unacceptable. Any project `./agent-browser.json`, explicit `--config` / `AGENT_BROWSER_CONFIG`, or `~/.agent-browser/config.json` discovered while planning disables managed restore without reading caller-selected content in the Pi host; owned spawns sticky-disable that session identity. Each subprocess that receives the wrapper restore key, plus every wrapper-owned close, overrides config discovery with a process-private empty `AGENT_BROWSER_CONFIG` (`0400` on POSIX) inside the canonical marked `0700` secure-temp root, closing the check-to-spawn race without trusting project or user config while retaining normal shutdown cleanup and PID/start-identity stale-root recovery after abnormal exit on POSIX and native Windows; versioned Windows identities treat legacy cross-format markers as unknown instead of incorrectly proving PID reuse, and temp ownership marker schema v2 makes older readers ignore new-format markers. Spawn-time revalidation rejects changed checkout identity, restore storage, unpinned launch-mutator environment, foreign managed-session targets, or forbidden managed-state access before agent-browser starts; the same check runs again after protected-config and socket-directory awaits immediately adjacent to the synchronous spawn. A failed fresh command that started agent-browser triggers an exact-identity daemon probe; an active or uninspectable daemon remains current and wrapper-owned so shutdown cleanup can close it, while pre-aborted and missing-binary calls remain unowned. Wrapper-owned close commands canonicalize upstream argv to JSON plus the known namespace/session and `close`, discarding caller config/restore globals, and do not inject a newly derived restore key into an existing daemon, so checkout replacement cannot make old auth save under the replacement generation; the close path retains the observed wrapper key long enough to record the returned old-generation snapshot safely. Because upstream writes a snapshot per daemon session, a successful wrapper-owned close requests JSON output and persists only the returned state path as an atomic record in a lockless convergent per-key ownership directory beside the snapshots (`0700`, with `0600` records, on POSIX). Cleanup carries that ownership proof across Pi restarts, self-heals malformed or stale regular records without claiming their snapshots, uses immutable atomic record names plus rescan-after-delete convergence so concurrent closers cannot skip ownership recording or exceed the aggregate cap, removes proven snapshots older than 30 days for the exact restore key while retaining the two newest, expires stale ownership-proven snapshots and empty manifests from other restore-key generations after 30 days only when a private lineage record proves the same canonical checkout path, caps young close churn at 256 records per key, and never deletes matching unrecorded files or the current checkout key. Upstream restore files under `~/.agent-browser/` remain plaintext unless `AGENT_BROWSER_ENCRYPTION_KEY` is set; before automatic managed restore the wrapper requires a durable Git generation and absolute platform home root; it pins the planned encryption-key value after caller env merging; on POSIX it also resolves `HOME` once, validates owner-trusted non-writable ancestry plus stable device/inode/birth-time metadata for both checkout and Git-admin directories, and pins that canonical value, enforces mode `0700` without silently repairing unsafe existing paths, and rejects symlinks/non-directories along the exact `~/.agent-browser[/namespaces/<canonical>/state]/sessions` path and its `.tmp` transactional-write area, while Windows requires an absolute `USERPROFILE` and the documented 64-character hex encryption key because POSIX mode checks cannot verify profile ACLs; malformed keys fail closed on every platform. POSIX process-start probes use absolute `/bin/ps` then `/usr/bin/ps`; if neither is available, managed policy locking fails closed with an actionable validation message. Managed `piab-r2-*` keys and key-bearing paths are redacted from visible/structured/JSON transcript surfaces. Malformed oversized upstream output is discarded after parsing rather than copied into a persistent parse-failure spill, and raw parse-failure stdout is omitted from result details. `session list` and `state list` filter wrapper-managed rows, and the pre-spawn policy blocks foreign managed restore/state references, broad clear/clean operations, and managed save/rename targets while preserving targeted caller-owned state workflows.
144
144
  - clean up process-private temp spill artifacts on shutdown, but keep persisted-session snapshot 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
145
145
  - 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)
146
- - reconstruct the current branch-visible extension-managed session, page-scoped refs, newest-revision aggregate artifact manifest, and Electron launch records from the active transcript branch on `session_start` and `session_tree` so later default calls keep following the active managed browser after resume/reload or branch switching; restore also honors successful explicit `--session <wrapper-owned> close` rows and `electron.cleanup` managed-session steps so closed wrapper-owned sessions are not resurrected
146
+ - 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, 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 and `electron.cleanup` managed-session steps so closed wrapper-owned sessions are not resurrected
147
147
  - 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, 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
148
148
  - record successful `connect`, `--cdp`, enabled `--auto-connect`, environment-configured CDP/auto-connect, and wrapper Electron attachment identities in branch-visible state, then run their main commands, helper probes, and cleanup inside an attached-browser process context that omits wrapper launch-only `--args` / `--allow-file-access` defaults. Re-sending those flags makes upstream choose local launch instead of its existing CDP connection and can trigger remote-debugging permission on every call. Block first-use attachment/content combinations until URL verification, and apply the existing live `get url` gate before every later content-bearing read or interaction because attached targets can drift outside Pi. Every child clears `AGENT_BROWSER_ALLOW_FILE_ACCESS`, including attached calls that omit the canonical launch flags. Successful close removes the marker; existing config, environment, URL, and protected-path checks still apply
149
149
  - 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
150
150
  - on non-quit shutdown such as `/reload`, close off-branch owned managed sessions and off-branch owned Electron launches before clearing process-local ownership, but preserve the current branch-visible active managed session and Electron launch plus that launch's isolated `userDataDir` so reload continuity still works from the active transcript branch
151
151
  - expose still-owned off-branch Electron launch records to `electron.status { launchId }`, `electron.status { all: true }`, `electron.probe { launchId }`, and `electron.cleanup`, while leaving default `electron.probe` scoped to the current managed session
152
- - if an unnamed fresh launch replaces an active extension-managed session, best-effort close the old managed session after the switch succeeds
152
+ - if an unnamed fresh launch replaces an active extension-managed session, best-effort close the old managed session after the switch succeeds; `managedSessionOutcome.replacedSessionClosed` records whether that cleanup succeeded, and a failed close keeps the older identity wrapper-owned across transcript resume for explicit follow-up or cleanup
153
153
  - leave explicit caller-provided `--session` choices alone unless the caller closes them explicitly, but before any content-bearing read or interaction against a caller-owned explicit session, live-probe that session with `get url` and apply the local-state boundary to the observed target instead of trusting missing or stale transcript page state; hold the effective canonical namespace/session queue from that probe through semantic snapshot resolution and the main command so another same-instance call cannot change tabs in between. Non-bail batch analysis retains every possible page left by a failed transition up to a fixed bound and blocks later content when any such page is local or unverified; exceeding the bound also fails closed to exact `batch --bail` guidance, while exact `batch --bail` or split calls make that dependency fail-safe. Protected Windows paths include drive-relative `C:...` forms, nested `batch` steps fail closed, and raw batch command strings mirror upstream's ASCII-space tokenizer, including quote/backslash handling, rather than splitting on other Unicode whitespace
154
154
  - after profiled `open` / `goto` / `navigate` calls, verify the active tab still matches the returned page URL and best-effort switch back when restored profile tabs steal focus
155
155
  - once the wrapper observes tab-drift risk for a session (profile restore correction, overlapping stale opens, or restored session state), later active-tab commands may synthesize a tiny upstream `batch` that re-selects that tab and then runs the requested command in the same upstream invocation; routine same-session commands avoid `tab list` preflights to reduce probes that can perturb upstream click behavior
@@ -157,7 +157,7 @@ Practical policy:
157
157
  - after successful `tab close`, read the now-active URL/title before updating per-session page state because upstream's close payload identifies the closed tab but does not report the remaining active tab; this keeps subsequent ref guards and interactions aligned without requiring a recovery snapshot solely to repair wrapper state
158
158
  - 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`), and refuse mutation-prone `@e…` argv before spawn when the active tab URL no longer matches the snapshot URL, when a ref id was never in that snapshot, or when `batch` stdin would reuse `@e…` on a guarded step after an earlier invalidating step without a later `snapshot` step in the same stdin array. 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
159
159
  - 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 uses that role/name metadata, including snapshot-order `duplicateIndex` for duplicate-name refs, instead of taking a fresh pre-click snapshot that could recycle upstream refs. The probe is intentionally skipped for CSS selector clicks, unresolved `find … click` locators, and `batch`/`job`/`qa` click steps
160
- - derive narrow prompt guards only for concrete evidence invariants: exact required screenshot paths block browser close until the artifact manifest verifies those paths. 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
160
+ - 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
161
161
  - after successful `get text` on a qualifying non-ref CSS selector, optionally issue one read-only `eval --stdin` probe per selector when multiple DOM matches or a hidden first match with visible peers could misread tabbed or off-screen content; simple id selectors and sensitive-looking literals skip this probe. Merge `details.selectorTextVisibility` / `selectorTextVisibilityAll`, visible warning lines, and `inspect-visible-text-candidates*` next actions as documented in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) and `RQ-0074` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
162
162
  - for local Unix launches, set a short private socket directory so extension-generated session names do not fail on the upstream Unix socket-path length limit; require the selected path to be absolute, owned by the current uid, mode `0700`, under trusted non-replaceable ancestry, and free of symlink, foreign-owner, or special planted entries; reject pre-existing unsafe modes instead of repairing them, then recheck before spawn
163
163
  - keep wrapper-spawned upstream CLI calls bounded by clamping `AGENT_BROWSER_DEFAULT_TIMEOUT` to the upstream documented 25-second default while deriving a longer subprocess watchdog for explicit long `wait <ms>` / `wait --timeout <ms>` calls; dialog commands, likely dialog-trigger clicks/taps/finds, and `eval --stdin` snippets that look like alert/confirm/prompt/dialog triggers use shorter wrapper subprocess budgets so blocking JavaScript prompts surface recovery actions before the full default watchdog
@@ -169,9 +169,9 @@ This is primarily about ownership clarity and avoiding surprise, not adding a he
169
169
  `agent-browser` startup flags are sticky once a session is already running.
170
170
  The extension should surface that clearly and avoid hidden restart behavior in v1.
171
171
 
172
- That means explicit startup-scoping flags like `--allowed-domains`, `--auto-connect`, `--cdp`, `--enable`, `--executable-path`, `--webgpu`, `--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.
172
+ That means explicit startup-scoping flags like `--allowed-domains`, `--auto-connect`, `--cdp`, `--enable`, `--executable-path`, `--webgpu`, `--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.
173
173
 
174
- 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 across helper and follow-up subprocesses, including branch reload/resume, because omitting it can make upstream relaunch the browser with a different launch identity. The process boundary also pins the same fixed compatibility value as a comma-safe Chrome launch argument for owned sessions, covering tabs and SSO popups that do not inherit upstream's per-page CDP override.
174
+ 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 across helper and follow-up subprocesses, including branch reload/resume, because omitting it can make upstream relaunch the browser with a different launch identity. The process boundary also pins the same fixed compatibility value as a comma-safe Chrome launch argument for owned sessions, covering tabs and SSO popups that do not inherit upstream's per-page CDP override. 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.
175
175
 
176
176
  If the implicit 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.
177
177
 
@@ -183,13 +183,14 @@ Implementation detail lives in `extensions/agent-browser/lib/launch-scoped-flags
183
183
  - **`--state` disambiguation:** Persisted browser `--state` before the command participates in launch-scoped validation and tab-correction hints. The same flag spelling after a `wait` command is excluded from startup-scoped detection so upstream help examples such as `wait @ref --state hidden` do not spuriously require `sessionMode: "fresh"` while an implicit session is active. As of the current upstream baseline, the parser still does not implement those `wait --state` examples as distinct wait modes, so agent-facing docs recommend `wait --fn` predicates for disappearance checks instead.
184
184
  - **`--auto-connect`:** Treated as launch-scoped only when enabled (`--auto-connect` bare or `true`). `--auto-connect false` is ignored for startup-scoped blocking so disabled attach hints do not force a fresh launch.
185
185
  - **`--webgpu`:** Treated as launch-scoped for both enabled and explicit `false` values. Enabled WebGPU selects upstream's platform-specific local-launch preset; explicit false can override an environment/config default and still belongs to a fresh browser launch. Upstream rejects enabled WebGPU with CDP, auto-connect, or provider launches.
186
+ - **`--headed`:** Treated as launch-scoped for both enabled and explicit `false` values so a visible-window choice cannot be silently ignored by an already-running managed session.
186
187
  - **`--allowed-domains`:** Treated as launch-scoped so containment cannot silently relaunch or reuse the active implicit browser. Upstream 0.32.0 owns request, worker, popup, and WebRTC containment and rejects CDP/auto-connect, profiles, restore/state replay, direct-page providers, iOS/Safari, and unsafe startup/profile Chrome args; the wrapper keeps only a final-URL policy check as defense in depth.
187
188
 
188
189
  **Sessionless inspection and local commands:** Plain-text global help and version probes (`--help`, `-h`, `--version`, `-V`) must never allocate or bind the extension-managed session. The same session-ownership rule applies to read-only upstream `skills list`, `skills get …`, and `skills path …`, local auth profile management (`auth save/list/show/delete/remove`), plus local/setup surfaces such as `profiles`, `dashboard start/stop`, `device list`, `doctor`, `install`, `upgrade`, `session id`, `session info`, `session list`, and caller-owned local saved-state maintenance (`state list/show`, targeted `state clear <caller-owned-name>`, and `state rename`). Broad clear/clean and managed-state targets remain syntactically sessionless but are rejected by the pre-spawn managed-state boundary. The same boundary rejects any discovered or explicit upstream config for browser-backed native calls without reading it, pins an empty private config for accepted browser-backed spawns to close config-creation races, and rejects browser access to `.agent-browser` local storage through command-specific input/output operands (including dash-prefixed values), their path-bearing environment mirrors (state/profile/config, executable/extension/init-script, action-policy, artifact, skills, and socket paths), encoded, nested-file-scheme, Windows-aliased, or symlinked paths (including nonexistent descendants of symlinked directories), protected top-level `outputPath`, content-returning local-URL calls, any follow-up on a local file page, and persisted unverified top-level or batch tab/attachment/script/state-load transitions, and recursively inspected raw batch command strings. Electron launch handoff, status/probe reads, and capture from a tracked protected target use the same guard; snapshot/tabs handoff and status/probe reads verify the live URL before tab/title/content helpers, while handoff failure or cancellation closes the managed session and host process/profile. Raw artifact destinations use the same command parser as preparation and are checked before it creates parent directories. Enabled `--allow-file-access` argv/env and raw Chrome file-access or protected-path values are rejected; every upstream spawn clears `AGENT_BROWSER_ARGS`, removes caller file-access occurrences, and adds canonical `--args "" --allow-file-access false` defaults so project/user config cannot override the boundary while a validated safe CLI `--args` value remains usable. Post-transition summaries, including forced live probes after arbitrary `eval`, verify URL before reading title and fail an implicit transition that lands on a local file page. Failed/unexecuted navigation stays unverified, stale completions serialize authoritative state only, and replay treats unknown state as dominant over inconsistent target/ref fields. `get url`, `tab list`, non-content `tab <id>` selection, explicit safe HTTP(S) navigation away, and close remain available; tab selection stays unverified until `get url` succeeds. `session list` and `state list` remove wrapper-managed rows, and explicit `piab-*` live-session targets require an ownership record from the current extension instance. Non-plain-text sessionless commands still run with `--json` for machine-readable output, but the planner does not prepend the implicit managed `--session`, so an agent can inspect local capabilities or start/stop the standalone dashboard without consuming the implicit session slot before a real `open`. Browser-backed, context-dependent, or incomplete commands such as root `session`, untargeted `state clear`, bare `state clean`, `auth login`, `state save`, and `state load` keep normal managed-session injection. Command-shape allowlisting lives in `extensions/agent-browser/lib/command-policy.ts` (`needsManagedSession`), while `extensions/agent-browser/lib/runtime.ts` (`isPlainTextInspectionArgs`, `buildExecutionPlan`) applies that decision to execution planning.
189
190
 
190
191
  A successful unnamed `sessionMode: "fresh"` launch should become the new extension-managed session so later default calls follow that browser instead of silently snapping back to the older managed session.
191
192
 
192
- When a managed implicit or fresh `--session` plan reaches process execution, `details.managedSessionOutcome` summarizes the managed-session transition: on **success**, statuses such as `created`, `replaced`, `unchanged`, or `closed` describe what became current (including successful close commands: `close`, `quit`, or `exit`); on **failure** (launch error, timeout, missing binary, failed close command, and similar), `preserved` vs `abandoned` captures whether a prior managed session stayed current or no managed session ended up active, plus related names and booleans. Post-launch failures after the fresh browser has already opened the target page—such as **`qa`** reclassification, a later failed `job`/batch step, or a fresh batch timeout with recovered current-page evidence—preserve that fresh session as current and set the visible recovery text to say the fresh launch became current instead of reverting the agent to the old session. Failing calls that used `sessionMode: "fresh"` also append a short `Managed session outcome: …` line to model-visible text so the next default `sessionMode: "auto"` hop is obvious; `"auto"` failures may still populate the struct without that extra line. Implementation and field semantics live in `extensions/agent-browser/lib/session-page-state.ts` and `extensions/agent-browser/lib/orchestration/browser-run/session-state.ts`; agent contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details); checklist row `RQ-0077` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md).
193
+ When a managed implicit or fresh `--session` plan reaches process execution, `details.managedSessionOutcome` summarizes the managed-session transition: on **success**, statuses such as `created`, `replaced`, `unchanged`, or `closed` describe what became current (including successful close commands: `close`, `quit`, or `exit`); on **failure** (launch error, timeout, missing binary, failed close command, and similar), `preserved` vs `abandoned` captures whether a prior managed session stayed current or no managed session ended up active, plus related names and booleans. Post-launch failures after the fresh browser has already opened the target page—such as **`qa`** reclassification, a later failed `job`/batch step, or a fresh batch timeout with recovered current-page evidence—preserve that fresh session as current and set the visible recovery text to say the fresh launch became current instead of reverting the agent to the old session. Failing calls that used `sessionMode: "fresh"`, plus otherwise-successful replacements whose automatic old-session close failed, append a short `Managed session outcome: …` line to model-visible text so the next default `sessionMode: "auto"` hop and any retained cleanup ownership are obvious; `"auto"` failures may still populate the struct without that extra line. Implementation and field semantics live in `extensions/agent-browser/lib/session-page-state.ts` and `extensions/agent-browser/lib/orchestration/browser-run/session-state.ts`; agent contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details); checklist row `RQ-0077` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md).
193
194
 
194
195
  ## Preferring the native tool
195
196
 
@@ -199,7 +200,7 @@ Keep the handling simple:
199
200
 
200
201
  This keeps the product centered on native tool usage instead of auxiliary skill wiring.
201
202
 
202
- Upstream restore-state persistence remains upstream-owned. The wrapper passes `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` through unchanged and may set `AGENT_BROWSER_RESTORE` for wrapper-owned managed sessions as described above; the behavior introduced in upstream 0.31.2 periodically saves restore-enabled cookies/localStorage after command quiet time and during idle page changes, while `0` disables periodic saves without disabling save-on-close. The wrapper must not duplicate that timer or treat upstream restore files as wrapper-owned artifacts.
203
+ Upstream restore-state persistence remains upstream-owned. The wrapper passes an explicit `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` through unchanged and may set `AGENT_BROWSER_RESTORE` for wrapper-owned managed sessions as described above. When no interval is set, wrapper-owned headed launches use `0` to avoid upstream 0.33.2's visible temporary multi-origin collector tabs and daemon-lock delays; the owned managed-session record persists that choice across every helper and follow-up subprocess, still-owned off-current session, Electron cleanup close, and transcript reload/resume so upstream never sees per-call daemon-configuration drift. Headless and caller-owned launches retain upstream's 30-second default. `0` preserves native close saves, but upstream exempts headed browsers from idle shutdown, so direct window close can lose changes since the last save. Because upstream reads the interval when its daemon starts, a nonzero explicit value cannot change an already-running wrapper-owned headed daemon; the wrapper preserves the recorded policy and requires close plus a fresh launch. The wrapper must not duplicate that timer or treat upstream restore files as wrapper-owned artifacts.
203
204
 
204
205
  ## Responsibility split
205
206
 
@@ -213,7 +214,6 @@ Upstream restore-state persistence remains upstream-owned. The wrapper passes `A
213
214
  - inline screenshots/images for the plain `screenshot` command; other image-like saves (for example `diff screenshot`) still appear in `details.artifacts` and summaries but are not auto-inlined as Pi image attachments (see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details))
214
215
  - lightweight session convenience
215
216
  - docs, including a repo-readable command reference that mirrors the blocked direct-binary help path closely enough for normal agent work
216
- - a deterministic **agent efficiency benchmark** (`scripts/agent-browser-efficiency-benchmark.mjs`) used to quantify representative agent-facing workflows without invoking upstream; maintainer commands and constraints are in [`AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md) under “Agent browser efficiency benchmark”
217
217
 
218
218
  ### Upstream `agent-browser` owns
219
219
 
@@ -22,7 +22,6 @@ This reference is baselined to the locally installed `agent-browser 0.33.2` comm
22
22
 
23
23
  The lightweight drift check is `npm run verify -- command-reference`. Run it whenever the installed upstream `agent-browser` version changes or this reference is edited.
24
24
 
25
- Use `npm run benchmark:agent-browser` or `npm run verify -- benchmark` before and after agent-facing workflow abstractions to measure task success, tool calls, model-visible output size, stale-ref behavior, artifact success, failure-category coverage, and elapsed-time estimates.
26
25
  <!-- agent-browser-capability-baseline:end upstream-baseline -->
27
26
 
28
27
  ### Upstream 0.33.2 rebaseline
@@ -69,7 +68,7 @@ The 0.31.2 rebaseline adds a WebGPU launch preset and periodic restore-state aut
69
68
  - `--webgpu` (also `AGENT_BROWSER_WEBGPU`; standalone upstream additionally accepts `"webgpu": true` in `agent-browser.json`) enables the upstream platform preset. Browser-backed native calls reject upstream config files, so use the flag or environment form through this tool. It uses Metal on macOS, D3D on Windows, and SwiftShader software Vulkan on Linux. The native Pi wrapper passes it through as a launch-scoped optional boolean, so use `sessionMode: "fresh"` after an implicit session exists; `--webgpu false` explicitly disables a config/environment default.
70
69
  - WebGPU requires a local browser launch. Upstream rejects enabled WebGPU with `--cdp`, `--auto-connect`, or `-p` / `--provider`. Use `doctor --webgpu` to pixel-check rendering and screenshot capture; use `doctor --webgpu --headed` when validating the headed capture path.
71
70
  - Headless WebGPU screenshots work on macOS. Upstream documents black headless WebGPU canvas captures on Windows and Linux even when in-page rendering succeeds; Windows needs a logged-in headed desktop, while Linux can use `--headed` with automatic Xvfb unless `AGENT_BROWSER_NO_XVFB=1`. Linux software rendering also needs `libvulkan1` and `mesa-vulkan-drivers`.
72
- - Restore-enabled sessions now save periodically while the browser remains open, including idle page-driven cookie/storage changes. `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` defaults to `30000`; `0` disables periodic saves but keeps save-on-close. The existing `--restore-save` policy still controls whether automatic saves are allowed.
71
+ - Restore-enabled sessions now save periodically while the browser remains open, including idle page-driven cookie/storage changes. `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` defaults to `30000`; `0` disables periodic saves but keeps native close saves. The existing `--restore-save` policy still controls whether automatic saves are allowed. For wrapper-owned headed launches, this extension defaults the interval to `0` because upstream 0.33.2 collects multi-origin storage through visible temporary tabs; upstream exempts headed browsers from idle shutdown, so direct window close can lose newer state unless an explicit interval was set before launch. The wrapper retains the effective launch-time interval across resume and rejects changes in either direction on a running wrapper-owned headed daemon until close plus a fresh launch.
73
72
  - Upstream MCP `open` and `doctor` tools now expose typed WebGPU fields. This Pi extension keeps `args` as the thin CLI-parity path; `mcp --help` remains sessionless, while bare long-running MCP server startup is intentionally rejected in a one-shot Pi tool call.
74
73
 
75
74
  ### Upstream 0.31.1 rebaseline
@@ -157,12 +156,12 @@ Tool parameters (use exactly one of `args`, `semanticAction`, `job`, `qa`, `sour
157
156
  - `networkSourceLookup`: **EXPERIMENTAL — candidates only** for failed request-to-source hints; compiles to generated `batch`, reports `details.compiledNetworkSourceLookup` and `details.networkSourceLookup`, and never assigns blame or edits files.
158
157
  - `electron`: optional Electron desktop-app shorthand. `list`, `status`, `cleanup`, and `probe` are wrapper-owned host/session helpers; `launch` starts a wrapper-owned isolated Electron profile and attaches through upstream `connect`.
159
158
  - `stdin`: only for `batch`, `eval --stdin`, and `auth save --password-stdin`; other command/stdin combinations are rejected before `agent-browser` is launched. `job`, `qa`, `sourceLookup`, `networkSourceLookup`, and `electron` generate or manage their own input.
160
- - `outputPath`: optional wrapper-owned local file sink for successful results. Use it for durable `eval`, `get`, `snapshot`, or diagnostic outputs; `details.outputFile` reports the saved path and byte count. If caller argv includes upstream `--json`, the visible JSON content stays parseable and the save notice is only in `details.outputFile`.
161
- - `timeoutMs`: optional per-call wrapper subprocess watchdog override in milliseconds for browser CLI modes. Use it for known-slow opens/captures rather than relying on repeated retries.
159
+ - `outputPath`: optional wrapper-owned local file sink for successful results. Use it for durable `eval`, `get`, `snapshot`, or diagnostic outputs, not as the destination for screenshots, downloads, recordings, or other browser artifacts; if the paths resolve to the same file, the browser artifact is preserved and the result-data write fails validation. `details.outputFile` reports the saved path and byte count. If caller argv includes upstream `--json`, the visible JSON content stays parseable and the save notice is only in `details.outputFile`.
160
+ - `timeoutMs`: optional per-call wrapper subprocess watchdog override in milliseconds for the requested browser CLI process. Managed-session policy inspection can independently consume up to 35 seconds before that process; this preflight is intentionally not shortened by `timeoutMs` because a busy but valid daemon must remain distinguishable from an unverifiable one.
162
161
  - `sessionMode`:
163
162
  - `"auto"` reuses the extension-managed session when possible.
164
- - `"fresh"` rotates that managed session to a fresh upstream launch so launch-scoped flags (`--auto-connect`, `--cdp`, `--enable`, `--executable-path`, `--webgpu`, `--init-script`, `--device`, `--namespace`, `--profile`, `--provider`, `-p`, `--restore`, `--restore-save`, `--restore-check-url`, `--restore-check-text`, `--restore-check-fn`, `--session-name`, `--state`) apply.
165
- - If a fresh launch fails or times out, read `details.managedSessionOutcome` for `preserved` vs `abandoned` (and related fields). A model-visible `Managed session outcome: …` line is appended only for failing calls that used `sessionMode: "fresh"`; `"auto"` failures can still populate the struct without that extra line. If you explicitly close the current wrapper-managed session with `--session <name> close`, later default auto calls rotate to a new wrapper-generated session instead of reusing the closed name; repeated closes and branch restores keep those generated names monotonic.
163
+ - `"fresh"` rotates that managed session to a fresh upstream launch so launch-scoped flags (`--allowed-domains`, `--auto-connect`, `--cdp`, `--enable`, `--executable-path`, `--webgpu`, `--init-script`, `--idle-timeout`, `--headed`, `--device`, `--namespace`, `--profile`, `--provider`, `-p`, `--restore`, `--restore-save`, `--restore-check-url`, `--restore-check-text`, `--restore-check-fn`, `--session-name`, `--state`) apply.
164
+ - If a fresh launch fails or times out, read `details.managedSessionOutcome` for `preserved` vs `abandoned` (and related fields). A model-visible `Managed session outcome: …` line is appended for failing calls that used `sessionMode: "fresh"` and when automatic close of a replaced session fails; `"auto"` failures can still populate the struct without that extra line. If you explicitly close the current wrapper-managed session with `--session <name> close`, later default auto calls rotate to a new wrapper-generated session instead of reusing the closed name; repeated closes and branch restores keep those generated names monotonic.
166
165
 
167
166
  ### Debug, diff, stream, dashboard, and chat families
168
167
 
@@ -190,6 +189,8 @@ Use upstream's global `--headed` flag on the first launch when the user needs to
190
189
  { "args": ["screenshot", "/tmp/agent-browser-headed-check.png"] }
191
190
  ```
192
191
 
192
+ `--headed` is launch-scoped, including explicit `false`, so changing an active managed session between headed and headless requires `sessionMode: "fresh"`. Wrapper-owned headed launches default `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` to `0` to avoid upstream 0.33.2's visible temporary storage-collector tabs and related daemon-policy delays, and record the effective launch-time interval and reapply it to every helper/follow-up subprocess, still-owned off-current session, Electron cleanup close, and reload/resume so daemon configuration remains stable. Native close still saves, but direct window close can lose newer state because headed browsers are exempt from idle shutdown; set an explicit interval before launch when periodic preservation matters, and close then relaunch fresh to change an existing daemon.
193
+
193
194
  For a WebGPU page, enable the launch preset before the first navigation. Treat it as local-launch-only and use a fresh managed session when changing an existing browser:
194
195
 
195
196
  ```json
@@ -816,7 +817,7 @@ When these commands are invoked through the native `agent_browser` tool, structu
816
817
  - project-local: `.pi/config/pi-agent-browser-native/config.json`
817
818
  - explicit override: `PI_AGENT_BROWSER_CONFIG=/path/to/config.json`
818
819
 
819
- Get an Exa API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys) or a Brave Search API key from the [Brave Search API dashboard](https://api-dashboard.search.brave.com/). If both keys are available, `agent_browser_web_search` prefers Exa by default because its `/search` endpoint returns token-efficient highlights and agent-oriented search modes; set `webSearch.preferredProvider` to `"brave"` when Brave Search is preferred. You can also disable this package's search tool with `webSearch.enabled: false` when another search tool should win. Config merges global → project → `PI_AGENT_BROWSER_CONFIG` override, so `enabled` is read from the final loaded config: a global disable can be re-enabled by project or override config, while an override file with `enabled: false` is the highest-priority hard disable for that run. Under Pi 0.79+, globally installed or CLI-loaded extensions are developer-trusted code, so this extension reads project-local config under `.pi/config/...` by default and skips that project layer when Pi reports the project is untrusted or when launched with `--no-approve`.
820
+ Get an Exa API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys) or a Brave Search API key from the [Brave Search API dashboard](https://api-dashboard.search.brave.com/). If both keys are available, `agent_browser_web_search` prefers Exa by default because its `/search` endpoint returns token-efficient highlights and agent-oriented search modes; set `webSearch.preferredProvider` to `"brave"` when Brave Search is preferred. You can also disable this package's search tool with `webSearch.enabled: false` when another search tool should win. Config merges global → project → `PI_AGENT_BROWSER_CONFIG` override, so `enabled` is read from the final loaded config: a global disable can be re-enabled by project or override config, while an override file with `enabled: false` is the highest-priority hard disable for that run. Under Pi 0.84.0+, globally installed or CLI-loaded extensions are developer-trusted code, so this extension reads project-local config under `.pi/config/...` by default and skips that project layer when Pi reports the project is untrusted or when launched with `--no-approve`.
820
821
 
821
822
  `pi install npm:pi-agent-browser-native` loads the extension, but it does **not** usually put the package helper on your shell `PATH`. The clearest setup is to write the config file directly and keep actual keys in the environment that launches `pi`:
822
823
 
package/docs/ELECTRON.md CHANGED
@@ -358,26 +358,10 @@ Before ending the task:
358
358
 
359
359
  If `cleanup` returns `failureCategory: "cleanup-failed"`, inspect `details.electron.cleanup.results[].steps` and use `retry-electron-cleanup` for the same `launchId`. Do not invent new cleanup commands for processes the wrapper did not start.
360
360
 
361
- ## Verification and benchmarks
361
+ ## Verification
362
362
 
363
- Electron support is gated by the same release evidence as the rest of the wrapper:
364
-
365
- - `RQ-0096` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md) records the contract, runtime, test, and verification coverage.
366
- - `electron-lifecycle` and `electron-probe` scenarios in `scripts/agent-browser-efficiency-benchmark.mjs` track the token-efficiency claim deterministically (no real browser, no real launches).
367
- - Fake-upstream Electron discovery and lifecycle coverage lives in `test/agent-browser.extension-electron-discovery.test.ts` and `test/agent-browser.extension-electron-lifecycle.test.ts`; ref/session recovery coverage lives in `test/agent-browser.extension-ref-guards.test.ts`, with shared validation paths in `test/agent-browser.extension-validation.test.ts`.
368
- - Real-app validation is a manual `tmux` smoke pass per the maintainer notes in `AGENTS.md`; the 2026-05-21 dogfood result and resulting decision are summarized in the repo-local [`Electron ADR`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/docs/plans/electron-extension-2026-05-20.md).
369
-
370
- Run the local gate the same way as the rest of the project:
371
-
372
- ```bash
373
- npm run verify
374
- ```
375
-
376
- The token-efficiency claim has its own opt-in run:
377
-
378
- ```bash
379
- npm run benchmark:agent-browser
380
- ```
363
+ - Fake-upstream Electron discovery/lifecycle tests cover list/launch/status/probe/cleanup without a real app.
364
+ - Real-app validation is a manual `tmux` smoke pass per `AGENTS.md`. Electron is a narrow typed lifecycle (`list`/`launch`/`status`/`probe`/`cleanup`) adopted because agents repeatedly failed the manual discover→debug-port→attach→cleanup sequence; it is not a generic recipe runtime (`RQ-0068` / `RQ-0096`).
381
365
 
382
366
  ## Where to go next
383
367
 
package/docs/RELEASE.md CHANGED
@@ -10,7 +10,7 @@ Related docs:
10
10
  - [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
11
11
  - Bounded `agent_browser` outcome metadata on `details` (`resultCategory`, `successCategory`, `failureCategory`, optional `nextActions`, optional `pageChangeSummary` with per-step summaries on `batch`): contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details); maintainer checklists under “Tool result categories” and “Page-change summaries” in [`../AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md)
12
12
  - Post-success `get text` selector visibility (`RQ-0074`): optional `details.selectorTextVisibility` / `selectorTextVisibilityAll`, visible warnings, and `inspect-visible-text-candidates*` next actions after read-only visibility probes—[`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md), [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), and [`../AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md) maintainer checklist
13
- - Managed-session outcomes (`RQ-0077`): after extension-managed implicit or fresh `--session` injection reaches process execution, `details.managedSessionOutcome` records the transition (`created` / `replaced` / `unchanged` / `closed` on success; `preserved` / `abandoned` when a plan fails before a new session becomes current). Failing `sessionMode: "fresh"` calls also append model-visible `Managed session outcome: …`—[`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md), [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md), and [`../AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md) maintainer checklist
13
+ - Managed-session outcomes (`RQ-0077`): after extension-managed implicit or fresh `--session` injection reaches process execution, `details.managedSessionOutcome` records the transition (`created` / `replaced` / `unchanged` / `closed` on success; `preserved` / `abandoned` when a plan fails before a new session becomes current). Failing `sessionMode: "fresh"` calls and successful replacements whose old-session close fails also append model-visible `Managed session outcome: …`—[`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md), [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md), and [`../AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md) maintainer checklist
14
14
  - Stateful context commands (`cookies`, `storage`, `auth`, `dialog`, `frame`, `state`) and aggregate `batch` results: model-facing `details.data` is summarized or redacted per [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details); aggregate `batch` replaces top-level `details.data` with a compact per-step matrix (`success`, argv-redacted `command`, redacted `result` or scrubbed `error`) while full per-step payloads, artifacts, and categories remain on `batchSteps[]`—operational notes in [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md#use-stateful-browser-context-commands-safely), assembly in `extensions/agent-browser/lib/results/presentation/batch.ts`
15
15
 
16
16
  ## Purpose
@@ -38,7 +38,7 @@ For PR-ready local confidence before release-only lifecycle and platform cost, r
38
38
  npm run verify -- pre-pr
39
39
  ```
40
40
 
41
- `pre-pr` composes the default gate with `npm run verify -- package`: generated docs, clean `dist/` build, TypeScript, the full unit/fake suite, live command-reference sampling, and package-content verification. It intentionally does not run lifecycle, packaged Pi smoke, Crabbox platform smoke, startup-profile, real-upstream, dogfood, or benchmark modes.
41
+ `pre-pr` composes the default gate with `npm run verify -- package`: generated docs, clean `dist/` build, TypeScript, the full unit/fake suite, live command-reference sampling, and package-content verification. It intentionally does not run lifecycle, packaged Pi smoke, Crabbox platform smoke, startup-profile, real-upstream or dogfood modes.
42
42
 
43
43
  `npm run verify -- release` runs:
44
44
 
@@ -49,7 +49,7 @@ npm run verify -- pre-pr
49
49
 
50
50
  `npm publish` runs npm’s `prepublishOnly` script from `package.json`, which executes the same `npm run verify -- release` gate and then `npm pack --dry-run`. That concatenated gate is everything in the default `npm run verify` step (generated playbook drift, clean `dist/` build, TypeScript, the unit/fake suite, generated command-reference blocks, and live upstream command-reference sampling against the targeted `agent-browser` on `PATH`), the configured-source lifecycle harness, the packaged Pi smoke in `package-pi`, and the release-blocking Crabbox platform matrix. Using `npm publish --ignore-scripts` skips that contract intentionally.
51
51
 
52
- `prepublishOnly` intentionally does **not** run the standalone host-only `npm run verify -- startup-profile`, `npm run verify -- real-upstream`, `npm run verify -- dogfood`, or `npm run verify -- benchmark` modes; those remain separate `npm run verify` modes in [`scripts/project.mjs`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/project.mjs). The platform matrix includes its own fast target-local build/package gate and browser dogfood suite, and is automated through the `release` slice.
52
+ `prepublishOnly` intentionally does **not** run the standalone host-only `npm run verify -- startup-profile`, `npm run verify -- real-upstream`, `npm run verify -- dogfood` modes; those remain separate `npm run verify` modes in [`scripts/project.mjs`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/project.mjs). The platform matrix includes its own fast target-local build/package gate and browser dogfood suite, and is automated through the `release` slice.
53
53
 
54
54
  Run the opt-in startup profiler whenever package layout, the compiled entrypoint, top-level imports, schema registration, or prompt/config startup logic changes:
55
55
 
@@ -81,7 +81,7 @@ Every release also requires interactive `tmux`-driven Pi dogfood with the native
81
81
 
82
82
  When reviewing saved session JSONL after a failed smoke or a `qa` preset that reclassified an upstream-successful batch, expect `agent_browser` tool rows to carry `isError: true` whenever `details.resultCategory` is `failure`. For normal prose output, model-visible text should end with a `Pi tool isError: true` category line; for caller-requested `--json` output, the hook preserves parseable JSON and only patches `isError`. The extension applies that patch on the `tool_result` path so Pi’s transcript matches the wrapper contract ([`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details)). Preserve a normal Pi session directory for those checks; avoiding `--no-session` keeps this evidence intact ([`AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md) preferred validation workflow).
83
83
 
84
- The configured-source lifecycle regression harness is required before release because it launches an interactive `pi` process under `tmux` with `--approve` and validates `/reload`, full relaunch with the same exact Pi 0.79 `--session-id`, managed-session continuity, persisted artifacts, compiled-entrypoint pickup after process restart, and Pi failure-patch behavior. Branch-backed `session_tree` rehydration and cleanup ownership are validated by focused extension harness tests:
84
+ The configured-source lifecycle regression harness is required before release because it launches an interactive Pi 0.84.0+ process under `tmux` with `--approve` and validates `/reload`, full relaunch with the same exact `--session-id`, managed-session continuity, persisted artifacts, compiled-entrypoint pickup after process restart, and Pi failure-patch behavior. Branch-backed `session_tree` rehydration and cleanup ownership are validated by focused extension harness tests:
85
85
 
86
86
  ```bash
87
87
  npm run verify -- lifecycle
@@ -155,17 +155,6 @@ Return a concise PASS/FAIL report with evidence and any tool or workflow issues
155
155
 
156
156
  Evaluator expectations after the queued Sauce Demo fixes: the agent should independently choose efficient, safe browser operations; native add-to-cart clicks should mutate cart state without the agent authoring `eval`/DOM-click fallbacks (the wrapper may fail with `details.clickDispatch` when upstream reports click success but no trusted DOM event reached the target); same-snapshot form fills may be batched safely when the agent chooses that route; the selected sort order should be verified; checkout must stop before Finish and must not place the order; the agent must not attempt Finish or another likely final submit action because prompt stop-boundaries are agent responsibility rather than wrapper-enforced business-intent policy; screenshot and recording must use the requested paths or be explicitly reported unavailable, and close should be blocked with `details.promptGuard.reason: "requested-artifacts-missing-before-close"` until required screenshot paths are verified; `network requests` may show public-demo telemetry 401s; `console` may report offline-cache logs; `errors` should show no page errors; and the browser session plus temp artifacts should be cleaned up after evidence is recorded. A run that reaches `checkout-complete.html` or silently substitutes artifact paths is a workflow failure even if other store flow steps work.
157
157
 
158
- ## Deterministic agent efficiency benchmark
159
-
160
- [`scripts/agent-browser-efficiency-benchmark.mjs`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/scripts/agent-browser-efficiency-benchmark.mjs) is an accounting-only benchmark: it does not shell out to `agent-browser`, launch a browser, or read or write Pi sessions. It models representative `agent_browser` call shapes (including optional `stdin` for `batch` and top-level `job`, `qa`, or experimental `sourceLookup` / `networkSourceLookup` objects that compile to batch) and aggregates success rate, tool-call counts, UTF-8 size of model-visible strings, stale-ref failure and recovery counts, artifact success, distinct failure-category coverage, and summed elapsed-time estimates. When extending scenarios, keep them aligned with the closed `RQ-0068` “no reusable recipe layer” rationale in [`ARCHITECTURE.md`](ARCHITECTURE.md#no-reusable-recipe-layer-yet) (benchmark ids cited there are the canonical inventory for that evidence bar).
161
-
162
- - **During development:** `npm run benchmark:agent-browser` prints a Markdown report; `npm run benchmark:agent-browser -- --json` saves machine-readable metrics; `npm run benchmark:agent-browser -- --compare path/to/prior.json` fails with exit code `1` on regressions (see the script’s `--help` for exit codes). Optional `--sample-jsonl path/to/session.jsonl` adds a `jsonlSample` section with real UTF-8 byte totals and per-workflow/overall p95 sizes for model-visible `agent_browser` tool-result text without changing deterministic scenario metrics; comparison ignores `jsonlSample` blocks.
163
- - **Default gate:** `npm run verify` checks generated playbook drift, clean-builds `dist/`, runs `tsc --noEmit`, runs the full unit/fake suite under `test/**/*.test.ts` with Node test concurrency pinned to `1` (including [`test/agent-browser.efficiency-benchmark.test.ts`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/test/agent-browser.efficiency-benchmark.test.ts) for scenario coverage and comparison behavior), verifies generated command-reference baseline blocks, and samples live upstream command-reference tokens. It does not spawn the standalone benchmark script’s JSON/Markdown run; that is what the opt-in slice below adds.
164
- - **Pre-PR gate:** `npm run verify -- pre-pr` runs the default gate plus `npm run verify -- package` for larger handoffs that need package-content confidence without lifecycle, platform, real-upstream, dogfood, or benchmark cost.
165
- - **Opt-in slice:** `npm run verify -- benchmark` runs the benchmark script once with `--json` and then that same test module alone. It is intentionally **not** part of `npm run verify -- pre-pr` or `npm run verify -- release`, so routine handoff and publish gates stay decoupled from benchmark churn while still allowing a focused check after editing scenarios or `CURRENT_BENCHMARK_VERSION`.
166
-
167
- Maintainer constraints for evolving scenarios and version bumps are summarized under “Agent browser efficiency benchmark” in [`../AGENTS.md`](https://github.com/fitchmultz/pi-agent-browser-native/blob/main/AGENTS.md).
168
-
169
158
  ## What package verification checks
170
159
 
171
160
  `npm run verify -- package` confirms that:
@@ -176,7 +165,7 @@ Maintainer constraints for evolving scenarios and version bumps are summarized u
176
165
  - `npm pack --json --dry-run` runs the package `prepare` build once and packs the compiled `dist/extensions/agent-browser/index.js` entrypoint
177
166
  - GitHub/source installs use the same `prepare` build; when Pi installs with `npm install --omit=dev`, `scripts/prepare.mjs` installs source-build dev dependencies with lifecycle scripts disabled before building so Pi can load the ignored compiled entrypoint from a fresh clone
178
167
  - the package-level doctor command and capability baseline are present
179
- - compiled extension runtime files are present, including the split result-rendering modules required by the published facade
168
+ - compiled extension runtime files are present, including the split result-rendering modules required by the compiled extension entrypoint
180
169
  - source-only, agent-only, and superseded docs are absent from the tarball
181
170
 
182
171
  `npm run verify -- package-pi` runs the same package-content checks and additionally confirms that:
@@ -192,7 +181,6 @@ The packaged execution smoke intentionally uses a temporary fake `agent-browser`
192
181
  Current forbidden packed files include:
193
182
 
194
183
  - `AGENTS.md`
195
- - internal planning docs under `docs/plans/`
196
184
  - `.pi/extensions/agent-browser.ts`
197
185
  - TypeScript extension source and other test/repo-only maintenance files
198
186
 
@@ -47,7 +47,7 @@ Define the product requirements and constraints for `pi-agent-browser-native`.
47
47
  ### Install priority
48
48
 
49
49
  - Prioritize the package install path first.
50
- - User-facing install docs should lead with `pi install npm:pi-agent-browser-native`; ephemeral package trials and validation should use `pi --no-extensions -e npm:pi-agent-browser-native[@<version>]` so configured checkout or global sources cannot duplicate `agent_browser`, adding `--approve` in Pi 0.79+ automation when the current project is intentionally trusted.
50
+ - User-facing install docs should lead with `pi install npm:pi-agent-browser-native`; ephemeral package trials and validation should use `pi --no-extensions -e npm:pi-agent-browser-native[@<version>]` so configured checkout or global sources cannot duplicate `agent_browser`, adding `--approve` in Pi 0.84.0+ automation when the current project is intentionally trusted.
51
51
  - User-facing install docs should also include the GitHub source path `pi install https://github.com/fitchmultz/pi-agent-browser-native`.
52
52
  - Provide a read-only package-level doctor command that checks upstream `agent-browser` PATH/version and duplicate Pi package/checkout sources before first use. It must not mutate Pi settings and must remain distinct from upstream `agent-browser doctor`.
53
53
  - Keep the current local-checkout path documented as the practical pre-release and development flow.
@@ -69,7 +69,7 @@ Define the product requirements and constraints for `pi-agent-browser-native`.
69
69
  - Supported actions, locators, exclusivity rules, when `details.compiledSemanticAction` appears, and bounded `try-*-candidate` follow-ups on `selector-not-found` (specific action/locator pairs only; see contract) are specified in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#semanticaction), with workflow examples in [`COMMAND_REFERENCE.md`](COMMAND_REFERENCE.md).
70
70
  - Constrained `job` remains a thin batch compiler, but its `click`/`fill` steps may use the same semantic locator fields as `semanticAction` so short workflows can avoid brittle selectors without adding a reusable recipe runtime, and `type` steps may expand to a bounded set of existing upstream focus/keyboard/wait/press rows for human-paced input while compacting model-visible batch text. `job` must default to fail-fast (`batch --bail`) so later mutating steps do not run after an earlier required step fails; `failFast: false` is the explicit opt-out.
71
71
  - `qa` must fail fast on failed readiness/text/selector assertions so missing expected text cannot burn the wrapper watchdog before reporting `qa-failure`. `qa.attached` must never erase existing session diagnostics; URL-opening `qa` may clear buffers to scope a fresh page load. Attached QA preserves buffers, reports that scope in `details.compiledQaPreset.checks.diagnosticsResetAtStart`, and defaults diagnostic checks off unless the caller opts into preserved-buffer failure checks.
72
- - Direct artifact workflows must create missing parent directories before spawning upstream and must verify saved files before downstream use. Simple loopback HTTP(S) anchor downloads may be saved directly by the wrapper to the requested path when that avoids upstream random-name download behavior without bypassing authenticated browser credentials. `outputPath` may write a successful result payload to a caller-requested local file and must report `details.outputFile`. Missing non-pending artifacts, including diff screenshot outputs, must never use saved/verified wording; `record start` future files are pending/open until `record stop`, not missing.
72
+ - Direct artifact workflows must create missing parent directories before spawning upstream and must verify saved files before downstream use. Simple loopback HTTP(S) anchor downloads may be saved directly by the wrapper to the requested path when that avoids upstream random-name download behavior without bypassing authenticated browser credentials. `outputPath` may write a successful result payload to a caller-requested local file and must report `details.outputFile`; it must never overwrite a screenshot, download, recording, or other browser artifact from the same result, including through a filesystem alias. Missing non-pending artifacts, including diff screenshot outputs, must never use saved/verified wording; `record start` future files are pending/open until `record stop`, not missing.
73
73
 
74
74
  ### Documentation standard
75
75
 
@@ -81,7 +81,7 @@ Define the product requirements and constraints for `pi-agent-browser-native`.
81
81
  - Because direct-binary usage is commonly blocked in normal agent sessions, the repo must carry a local command reference for the effective `agent_browser` surface and keep it in sync with upstream changes.
82
82
  - Repository verification must include a lightweight command-reference drift check against the targeted installed upstream `agent-browser` version.
83
83
  - Published package contents should include the canonical user-facing docs plus `LICENSE`.
84
- - Published package contents should exclude agent-only and internal planning docs such as `AGENTS.md` and `docs/plans/`.
84
+ - Published package contents should exclude agent-only and internal planning docs such as `AGENTS.md`.
85
85
 
86
86
  ### Testing guidance
87
87
 
@@ -127,6 +127,6 @@ The design should comfortably support workflows such as:
127
127
  - On local Unix launches, extension-generated session names should not fail just because the upstream default socket path is too long; the wrapper should choose a shorter socket directory when needed.
128
128
  - Provider selection flags (`-p`, `--provider`) and provider device flags (`--device`) are launch-scoped like profile, CDP, persisted state, and upstream's `--webgpu` launch preset: if an extension-managed implicit session is already active, the planner must fail fast with the same recovery guidance as other startup-scoped flags instead of silently forwarding argv upstream would ignore; contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#sessionmode) and session model in [`ARCHITECTURE.md`](ARCHITECTURE.md).
129
129
  - Treat argv-supplied `--allowed-domains` as launch-scoped so it starts in a fresh controllable browser context. Keep upstream 0.32.0 responsible for request/worker/popup/WebRTC containment and incompatible launch-mode rejection; retain the wrapper's final-URL policy check only as defense in depth.
130
- - Upstream restore-state periodic autosave remains upstream-owned. Forward `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` unchanged, document its 30-second default and `0` disable behavior, and do not duplicate its timer or claim its restore files as wrapper artifacts.
130
+ - Upstream restore-state periodic autosave remains upstream-owned. Forward an explicit `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` unchanged when a daemon launches; otherwise default it to `0` for wrapper-owned headed launches, persist the effective launch-time interval, and retain that environment across helpers, follow-ups, still-owned off-current sessions, Electron cleanup closes, and transcript reload/resume to avoid upstream 0.33.2's visible temporary collector tabs and per-call daemon-configuration drift. Reject attempts to change that interval on an already-running wrapper-owned headed daemon until the caller closes it and launches fresh. If automatic cleanup of a replaced wrapper-owned session fails, persist that outcome and restore the older identity's ownership from the transcript so explicit follow-up and cleanup remain possible. Document the 30-second upstream default, native-close preservation, and the direct-window-close loss risk because headed browsers are exempt from idle shutdown; do not duplicate its timer or claim its restore files as wrapper artifacts.
131
131
  - Read-only upstream `skills list`, `skills get …`, and `skills path …` must stay free of implicit managed `--session` under default `sessionMode: "auto"` (still with `--json`), matching plain-text `--help` / `--version` inspection semantics so bundled skill text does not pin or rotate the active browser session; new `skills` subcommands pick up that behavior only after allowlisting in `extensions/agent-browser/lib/runtime.ts` with regression coverage.
132
132
  - Optional `semanticAction.session` on native `agent_browser` must compile to a leading `--session <name>` pair before upstream `find` or `select` argv so the shorthand can target a named upstream browser without hand-built `args`, while `buildExecutionPlan` still skips double-injecting the extension-managed implicit session whenever planned argv already starts with `--session`; stale-ref retries for compiled `find` actions and bounded `try-*` candidate `nextActions` must preserve that same prefix. Contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#semanticaction) / [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#sessionmode); implementation in `extensions/agent-browser/index.ts` and `extensions/agent-browser/lib/runtime.ts`.