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.
- package/CHANGELOG.md +27 -0
- package/README.md +8 -17
- package/dist/extensions/agent-browser/index.js +127 -19
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +0 -5
- package/dist/extensions/agent-browser/lib/argv-grammar.js +4 -5
- package/dist/extensions/agent-browser/lib/command-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +0 -6
- package/dist/extensions/agent-browser/lib/config-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/config.js +0 -6
- package/dist/extensions/agent-browser/lib/electron/cdp.js +6 -7
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/discovery.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/launch.js +0 -7
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/job.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -6
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/types.js +0 -5
- package/dist/extensions/agent-browser/lib/json-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +4 -5
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +0 -3
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +30 -13
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +0 -5
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +1 -7
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +0 -5
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +41 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +17 -11
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +22 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +35 -7
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +17 -1
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +35 -18
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +4 -1
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
- package/dist/extensions/agent-browser/lib/parsing.js +0 -7
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +4 -1
- package/dist/extensions/agent-browser/lib/playbook.js +3 -10
- package/dist/extensions/agent-browser/lib/process-identity.js +0 -5
- package/dist/extensions/agent-browser/lib/process.js +0 -7
- package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -8
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +6 -7
- package/dist/extensions/agent-browser/lib/results/categories.js +1 -8
- package/dist/extensions/agent-browser/lib/results/confirmation.js +0 -7
- package/dist/extensions/agent-browser/lib/results/contracts.js +0 -7
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +0 -7
- package/dist/extensions/agent-browser/lib/results/envelope.js +0 -7
- package/dist/extensions/agent-browser/lib/results/network.js +0 -7
- package/dist/extensions/agent-browser/lib/results/next-actions.js +0 -30
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +1 -7
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation.js +0 -5
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot.js +0 -7
- package/dist/extensions/agent-browser/lib/results/text.js +0 -7
- package/dist/extensions/agent-browser/lib/runtime.js +4 -14
- package/dist/extensions/agent-browser/lib/session-page-state.js +0 -7
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/temp.js +0 -7
- package/dist/extensions/agent-browser/lib/web-search.js +0 -5
- package/docs/ARCHITECTURE.md +16 -16
- package/docs/COMMAND_REFERENCE.md +8 -7
- package/docs/ELECTRON.md +3 -19
- package/docs/RELEASE.md +5 -17
- package/docs/REQUIREMENTS.md +4 -4
- package/docs/SUPPORT_MATRIX.md +17 -17
- package/docs/TOOL_CONTRACT.md +18 -16
- package/docs/platform-smoke.md +1 -1
- package/package.json +5 -6
- package/scripts/doctor.mjs +1 -1
- package/dist/extensions/agent-browser/lib/electron/text.js +0 -13
- package/dist/extensions/agent-browser/lib/input-modes.js +0 -10
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +0 -5
- package/dist/extensions/agent-browser/lib/results/artifact-state.js +0 -13
- package/dist/extensions/agent-browser/lib/results.js +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.3.0 - 2026-08-06
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Raised the minimum supported Pi runtime to 0.84.0 with no compatibility shims for older Pi releases, bumped the package from 0.2.x to 0.3.0 for the breaking support-floor change, pinned direct Pi development dependencies and the fleet marker to 0.84.0, and retained optional wildcard Pi peer dependencies per Pi package guidance.
|
|
10
|
+
- Audited the extension factory, native tool registration, schemas, Pi `tool_result` patching, TUI rendering, SDK/package harnesses, browser/session/profile lifecycle, artifacts, lookups, Electron paths, build/package scripts, docs, fixtures, and tests against all Pi 0.84.0 breaking changes. The package does not consume the renamed model transform, RPC delta accumulator, provider header/refresh/auth APIs, pi-agent-core harness repositories or custom filesystem, or remote-session summary APIs; the existing coding-agent `ModelRuntime`, `createAgentSession`, `SessionManager`, extension, and tool contracts remain valid on 0.84.0.
|
|
11
|
+
- Added Pi 0.84.0's `scrollbarThumb` background color to the complete test theme fixture and refreshed the lockfile against the released 0.84.0 packages and TypeBox 1.3.7.
|
|
12
|
+
- Updated the `protobufjs` safety override to 7.6.5, clearing the advisory carried by the previous 7.6.4 pin.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Result `outputPath` writes now fail validation instead of overwriting a screenshot, download, recording, or other browser artifact when both destinations resolve to the same file; the browser artifact and its verified metadata remain intact, including through filesystem aliases such as hard links.
|
|
17
|
+
|
|
18
|
+
- Headed wrapper-managed launches now disable upstream periodic restore autosave by default and retain that launch environment across every follow-up subprocess, including still-owned off-current sessions, transcript-restored sessions whose replacement cleanup failed, and Electron cleanup closes, preventing agent-browser 0.33.2's multi-origin storage collector from flashing temporary tabs, blocking daemon policy probes, or triggering daemon-configuration mismatches; native close still saves, while direct window close can lose newer state because headed browsers are exempt from idle shutdown. The effective launch-time interval, including an explicit `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS`, is persisted across transcript resume; changing it in either direction on a running wrapper-owned headed session is rejected until close plus a fresh launch. Slow valid daemon inspections now receive the full 35-second policy budget instead of failing after five seconds.
|
|
19
|
+
- `--headed` and `--headed false` are now enforced as launch-scoped choices instead of being silently sent to an already-running managed session.
|
|
20
|
+
- Bare, review-only, fenced-reference, conditional, permissive/uncertain, directly negated, and Pi clipboard/attachment image/video paths no longer become requested output artifacts that block browser close; output enforcement now requires a direct artifact-creation phrase with a destination, carries that intent across contiguous plain or Markdown path-list lines, handles delimited paths, preserves subordinate requirements such as “do not close until you save,” scopes availability-qualified recordings before, within, or after their list, leaves explicitly optional artifacts unenforced, applies recording availability per path clause, makes required duplicate paths take precedence, handles Markdown-link destinations, scans single-line and multiline path lists once instead of once per path, and avoids pathological backtracking on slash-heavy non-path text.
|
|
21
|
+
|
|
22
|
+
## 0.2.78 - 2026-08-04
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Removed zero-behavior maintainer bloat: Purpose/Responsibilities file banners, barrel-only `lib/results.ts` and `lib/input-modes.ts` facades, the synthetic efficiency benchmark script/tests/verify mode, the completed Electron plan ADR, and the `AgentBrowserNextActionCollector` class (plain array helpers remain).
|
|
27
|
+
- Folded tiny one-liner modules (`session-artifacts`, `artifact-state`, `electron/text`) into neighboring owners; pending-recording predicates live in `artifact-manifest.ts`.
|
|
28
|
+
- Trimmed docs that only existed to index or advertise the removed surfaces (`AGENTS.md`, `RELEASE.md`, `SUPPORT_MATRIX.md`, `ARCHITECTURE.md`, `ELECTRON.md`, `TOOL_CONTRACT.md`, `COMMAND_REFERENCE.md`, README).
|
|
29
|
+
|
|
3
30
|
## 0.2.77 - 2026-08-04
|
|
4
31
|
|
|
5
32
|
### Fixed
|
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ The result is optimized for agent work:
|
|
|
89
89
|
|
|
90
90
|
## Fastest way to try it
|
|
91
91
|
|
|
92
|
-
Use Pi 0.
|
|
92
|
+
Use Pi 0.84.0 or newer. This package keeps optional Pi core imports as wildcard `peerDependencies` because Pi package docs require the host Pi install to provide those packages, pins its direct Pi validation dependencies to 0.84.0, and makes older hosts a setup failure through `pi-agent-browser-doctor`. Version 0.3.0 intentionally provides no compatibility shims for older Pi releases.
|
|
93
93
|
|
|
94
94
|
Install upstream `agent-browser` first and make sure it is on `PATH`:
|
|
95
95
|
|
|
@@ -125,7 +125,7 @@ For a one-off trial that does not touch your configured Pi extensions:
|
|
|
125
125
|
pi --no-extensions -e npm:pi-agent-browser-native
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
Pi 0.
|
|
128
|
+
Pi 0.84.0+ may ask whether to trust the current project before loading project-local instructions, settings, or resources. This extension treats its own project-local package config as developer-trusted by default; use `--no-approve` when you intentionally want Pi and this extension to ignore project-local inputs for that run.
|
|
129
129
|
|
|
130
130
|
For a specific published version:
|
|
131
131
|
|
|
@@ -234,7 +234,7 @@ printf '%s' "$EXA_API_KEY" | npm exec --yes --package pi-agent-browser-native@la
|
|
|
234
234
|
npm exec --yes --package pi-agent-browser-native@latest -- pi-agent-browser-config web-search set-command "op read 'op://Private/Brave Search/API Key'" --provider brave --global
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
Config merges in this order: global → project → `PI_AGENT_BROWSER_CONFIG` override. Under Pi 0.
|
|
237
|
+
Config merges in this order: global → project → `PI_AGENT_BROWSER_CONFIG` override. Under Pi 0.84.0+, the globally installed or CLI-loaded extension still loads project-local `.pi/config/pi-agent-browser-native/config.json` when Pi trust allows that project layer; it skips that project layer when Pi reports the project is untrusted or when Pi is launched with `--no-approve`. `webSearch.enabled` is evaluated after the loaded layers merge. Use `web-search disable --global` for a user default, `web-search disable --project` for one repo, and a `PI_AGENT_BROWSER_CONFIG` override with `{ "webSearch": { "enabled": false } }` when web search must stay off even if project config exists. Loaded config may use plaintext, custom environment aliases, interpolation literals, malformed-or-late-bound `$` values, and `!command` credential sources; the resolved secret is passed to the provider request while tool content, details, status output, and docs examples stay redacted. `web-search set-key`, `set-command`, and `clear` require `--provider`; `set-env` infers Exa/Brave from `EXA_API_KEY` or `BRAVE_API_KEY` unless you pass `--provider`.
|
|
238
238
|
|
|
239
239
|
For Exa, the tool defaults to `searchType: "auto"` with `contents.highlights: true`. Agents may pass `searchType` (`fast`, `instant`, `deep-lite`, `deep`, or `deep-reasoning`) only when the task needs that latency/depth tradeoff; structured output schemas are intentionally not exposed yet.
|
|
240
240
|
|
|
@@ -268,6 +268,8 @@ Watch a browser window during a demo or QA run by adding upstream's global `--he
|
|
|
268
268
|
{ "args": ["screenshot", "/tmp/agent-browser-headed-check.png"] }
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
+
For wrapper-owned headed launches, the extension disables upstream 0.33.2 periodic restore autosave by default because its multi-origin collector opens visible temporary tabs and can delay daemon policy inspection. The extension records the effective launch-time interval and reapplies it to every helper and follow-up subprocess, including still-owned off-current sessions (also after failed replacement cleanup), Electron cleanup closes, and reload/resume, so the receiving daemon does not see changing configuration. Native `close` still saves, but upstream exempts headed browsers from idle shutdown, so closing the window by hand can lose newer state. Set `AGENT_BROWSER_AUTOSAVE_INTERVAL_MS` before launch when periodic preservation matters; changing it on a running wrapper-owned headed daemon is rejected until you close that session and launch fresh.
|
|
272
|
+
|
|
271
273
|
Render a WebGPU page by enabling upstream's WebGPU launch preset on a fresh local browser:
|
|
272
274
|
|
|
273
275
|
```json
|
|
@@ -321,7 +323,7 @@ Evaluate page JavaScript through stdin. Put the script in the top-level `stdin`
|
|
|
321
323
|
{ "args": ["eval", "--stdin"], "stdin": "({ title: document.title, url: location.href })", "outputPath": "logs/page-state.json" }
|
|
322
324
|
```
|
|
323
325
|
|
|
324
|
-
Use `outputPath` when `eval`, `get`, `snapshot`, or another extraction should be saved as a durable workspace file. The wrapper writes `details.data` when present, otherwise the model-facing text content, and returns `details.outputFile` with the saved path and byte count. Explicit upstream `--json` content stays parseable; in that case the save notice lives only in `details.outputFile`.
|
|
326
|
+
Use `outputPath` when `eval`, `get`, `snapshot`, or another extraction should be saved as a durable workspace file. Keep it distinct from screenshot, download, recording, and other browser artifact destinations; if the paths resolve to the same file, the wrapper preserves the browser artifact and rejects the result-data write. The wrapper writes `details.data` when present, otherwise the model-facing text content, and returns `details.outputFile` with the saved path and byte count. Explicit upstream `--json` content stays parseable; in that case the save notice lives only in `details.outputFile`.
|
|
325
327
|
|
|
326
328
|
Extract several known refs or selectors in one `batch` call instead of many serial getter calls:
|
|
327
329
|
|
|
@@ -440,7 +442,7 @@ After either path, use `qa: { "attached": true, ... }` for a current-session smo
|
|
|
440
442
|
|
|
441
443
|
### Lightweight QA preset
|
|
442
444
|
|
|
443
|
-
For a quick smoke/QA pass, use top-level `qa`. It compiles to the same batch path as `job` and uses `batch --bail` so failed readiness/text/selector assertions stop before slower diagnostics can burn the wrapper watchdog. The URL form clears enabled network/console/page-error buffers before opening the target URL, waits for page readiness, checks optional expected text or selector, inspects fresh network requests, console messages, and page errors when preceding assertions pass, and can capture an evidence screenshot. Successful reset rows are labeled as reset-scoped output and ignored by QA failure analysis so stale pre-target errors do not fail an otherwise healthy target page; real post-open diagnostic rows still fail or warn according to the normal QA rules. Expected text is checked with bounded visible-text `wait --fn … --timeout 5000` predicates after the requested load state so dense pages can pass on visible headings/copy and missing text becomes crisp QA evidence. The attached form (`qa: { "attached": true }`) runs checks against the current managed session, such as an attached Electron app, rejects `url`, and deliberately preserves existing diagnostics instead of clearing evidence; its diagnostic reads default off so stale buffers do not fail a current-page smoke unless `checkNetwork`, `checkConsole`, or `checkErrors` is explicitly `true`. `loadState` defaults to `"domcontentloaded"`; set it to `"load"` or `"networkidle"` only when the stricter state is useful and the site is not expected to keep background requests alive. For URL-opening QA, `checkNetwork`, `checkConsole`, and `checkErrors` default to true; set one to `false` to skip that diagnostic read. Network failures are classified by likely impact and failed rows are listed first in network previews: actionable document/script/API-style failures still fail QA, while some low-impact browser icon asset misses (for example certain `favicon` or `apple-touch-icon` paths when upstream marks the row failed and resource metadata looks image-like) surface only as warnings instead of failing an otherwise healthy smoke check (`details.qaPreset.warnings`, with human-readable `details.qaPreset.summary` when the preset still passes). Exact predicates live in [`docs/TOOL_CONTRACT.md`](docs/TOOL_CONTRACT.md#qa) and `classifyNetworkRequestFailure` in `extensions/agent-browser/lib/results/network.ts
|
|
445
|
+
For a quick smoke/QA pass, use top-level `qa`. It compiles to the same batch path as `job` and uses `batch --bail` so failed readiness/text/selector assertions stop before slower diagnostics can burn the wrapper watchdog. The URL form clears enabled network/console/page-error buffers before opening the target URL, waits for page readiness, checks optional expected text or selector, inspects fresh network requests, console messages, and page errors when preceding assertions pass, and can capture an evidence screenshot. Successful reset rows are labeled as reset-scoped output and ignored by QA failure analysis so stale pre-target errors do not fail an otherwise healthy target page; real post-open diagnostic rows still fail or warn according to the normal QA rules. Expected text is checked with bounded visible-text `wait --fn … --timeout 5000` predicates after the requested load state so dense pages can pass on visible headings/copy and missing text becomes crisp QA evidence. The attached form (`qa: { "attached": true }`) runs checks against the current managed session, such as an attached Electron app, rejects `url`, and deliberately preserves existing diagnostics instead of clearing evidence; its diagnostic reads default off so stale buffers do not fail a current-page smoke unless `checkNetwork`, `checkConsole`, or `checkErrors` is explicitly `true`. `loadState` defaults to `"domcontentloaded"`; set it to `"load"` or `"networkidle"` only when the stricter state is useful and the site is not expected to keep background requests alive. For URL-opening QA, `checkNetwork`, `checkConsole`, and `checkErrors` default to true; set one to `false` to skip that diagnostic read. Network failures are classified by likely impact and failed rows are listed first in network previews: actionable document/script/API-style failures still fail QA, while some low-impact browser icon asset misses (for example certain `favicon` or `apple-touch-icon` paths when upstream marks the row failed and resource metadata looks image-like) surface only as warnings instead of failing an otherwise healthy smoke check (`details.qaPreset.warnings`, with human-readable `details.qaPreset.summary` when the preset still passes). Exact predicates live in [`docs/TOOL_CONTRACT.md`](docs/TOOL_CONTRACT.md#qa) and `classifyNetworkRequestFailure` in `extensions/agent-browser/lib/results/network.ts`.
|
|
444
446
|
|
|
445
447
|
```json
|
|
446
448
|
{
|
|
@@ -606,17 +608,6 @@ npm run verify -- pre-pr
|
|
|
606
608
|
|
|
607
609
|
That mode composes the full default gate with `npm run verify -- package`, so package contents and forbidden repo-only files are checked without launching Pi lifecycle, Crabbox, or live dogfood flows. Package modes build through npm `prepare`; lifecycle and startup-profile build in their focused scripts; default and platform-target build before consuming `dist/`, so clean checkouts do not validate stale or missing compiled output. The same `prepare` script owns GitHub/source installs; when Pi installs with `npm install --omit=dev`, it installs the source-build dev dependencies with lifecycle scripts disabled before building the ignored `dist/` entrypoint that Pi loads.
|
|
608
610
|
|
|
609
|
-
The deterministic agent-efficiency benchmark’s **standalone JSON/Markdown accounting run** is not part of default or pre-PR `npm run verify` (only `npm run verify -- benchmark` or `npm run benchmark:agent-browser` invokes the script). The full unit suite still exercises `test/agent-browser.efficiency-benchmark.test.ts`. Use the script before and after agent-facing abstractions to prove call-count, output-size, stale-ref, artifact, failure-category coverage, success-rate, and elapsed-time effects before changing the wrapper UX:
|
|
610
|
-
|
|
611
|
-
```bash
|
|
612
|
-
npm run benchmark:agent-browser
|
|
613
|
-
npm run verify -- benchmark
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
Save a JSON baseline (for example before changing playbook or wrapper behavior), then compare later runs: `npm run benchmark:agent-browser -- --json > /tmp/agent-browser-benchmark.json` and `npm run benchmark:agent-browser -- --compare /tmp/agent-browser-benchmark.json`.
|
|
617
|
-
|
|
618
|
-
It does not launch a browser or mutate local profiles; it models representative raw workflows and provides a stable baseline for later comparisons.
|
|
619
|
-
|
|
620
611
|
The opt-in startup profiler measures only the package extension entrypoint import plus factory registration in fresh Node processes. It intentionally does **not** launch Pi, tmux, mise, npm, browsers, or `agent-browser`; full Pi TUI ready-prompt profiling proved too invasive for routine verification on the operator machine. Run it after package entrypoint, generated runtime, or top-level import changes:
|
|
621
612
|
|
|
622
613
|
```bash
|
|
@@ -704,7 +695,7 @@ Configured-source lifecycle validation:
|
|
|
704
695
|
npm run verify -- lifecycle
|
|
705
696
|
```
|
|
706
697
|
|
|
707
|
-
The harness defaults to Pi model `zai/glm-5.2` and **180000 ms** per-step tmux waits; pass `--model <id>` and/or `--timeout-ms <ms>` after `lifecycle` when you need different settings (see [Configured-source lifecycle validation](docs/RELEASE.md#configured-source-lifecycle-validation) in `docs/RELEASE.md`). It launches Pi
|
|
698
|
+
The harness defaults to Pi model `zai/glm-5.2` and **180000 ms** per-step tmux waits; pass `--model <id>` and/or `--timeout-ms <ms>` after `lifecycle` when you need different settings (see [Configured-source lifecycle validation](docs/RELEASE.md#configured-source-lifecycle-validation) in `docs/RELEASE.md`). It launches the supported Pi runtime with `--approve` and a deterministic `--session-id`, drives `/reload`, closes Pi, relaunches the exact same session, asserts the JSONL header id, and checks managed-session continuity, compiled-entrypoint pickup after process restart, persisted spill reachability, and real Pi `tool_result` failure-patch behavior.
|
|
708
699
|
|
|
709
700
|
Use lifecycle validation when testing `/reload`, exact-session relaunch, `/resume`, managed-session continuity, or persisted artifact behavior. Branch-backed state and `session_tree` cleanup ownership are covered by focused extension harness tests. Maintainers must run the lifecycle harness before every publish; see [Pre-release checks](docs/RELEASE.md#pre-release-checks).
|
|
710
701
|
|
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Register the native agent_browser tool for pi so agents can invoke agent-browser without going through bash.
|
|
3
|
-
* Responsibilities: Define the tool schema, inject thin wrapper behavior around the upstream CLI, manage extension-owned browser session convenience, and return pi-friendly content/details.
|
|
4
|
-
* Scope: Native tool registration and orchestration only; the wrapper intentionally stays close to the upstream agent-browser CLI.
|
|
5
|
-
* Usage: Loaded by pi through the package manifest in this package, or explicitly via `pi --no-extensions -e .` during local checkout development.
|
|
6
|
-
* Invariants/Assumptions: agent-browser is installed separately on PATH, the wrapper targets the current locally installed upstream version only, and no backward-compatibility shims are provided.
|
|
7
|
-
*/
|
|
8
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
2
|
import { dirname, join } from "node:path";
|
|
10
3
|
import { fileURLToPath } from "node:url";
|
|
11
4
|
import { Text } from "@earendil-works/pi-tui";
|
|
12
5
|
import { PROJECT_RULE_PROMPT, buildBrowserDefaultProfileGuideline, buildBrowserExecutablePathGuideline, buildToolPromptGuidelines, } from "./lib/playbook.js";
|
|
13
6
|
import { SessionPageState } from "./lib/session-page-state.js";
|
|
14
|
-
import { canUseHeadlessCompatibilityUserAgent, createEphemeralSessionSeed, createFreshSessionName, createImplicitSessionName, extractCommandTokens, getImplicitSessionCloseTimeoutMs, getImplicitSessionIdleTimeoutMs, restoreManagedSessionStateFromBranch, validateToolArgs, } from "./lib/runtime.js";
|
|
15
|
-
import { extractExplicitNamespace, extractExplicitSessionName, resolveAgentBrowserNamespace } from "./lib/argv-grammar.js";
|
|
7
|
+
import { canUseHeadlessCompatibilityUserAgent, createEphemeralSessionSeed, createFreshSessionName, createImplicitSessionName, extractCommandTokens, getImplicitSessionCloseTimeoutMs, getImplicitSessionIdleTimeoutMs, isRestorableManagedSessionName, restoreManagedSessionStateFromBranch, validateToolArgs, } from "./lib/runtime.js";
|
|
8
|
+
import { extractExplicitNamespace, extractExplicitSessionName, isUpstreamEnvFlagEnabled, resolveAgentBrowserNamespace } from "./lib/argv-grammar.js";
|
|
16
9
|
import { cleanupManagedSessionRestoreConfig, ManagedSessionRestoreState } from "./lib/managed-session-restore.js";
|
|
17
10
|
import { isRecord } from "./lib/parsing.js";
|
|
18
11
|
import { buildPromptPolicy, getLatestUserPrompt, shouldAppendBrowserSystemPrompt } from "./lib/prompt-policy.js";
|
|
19
12
|
import { isCloseCommand } from "./lib/command-taxonomy.js";
|
|
20
13
|
import { hasLaunchScopedFlagToken } from "./lib/launch-scoped-flags.js";
|
|
21
14
|
import { cleanupSecureTempArtifacts } from "./lib/temp.js";
|
|
22
|
-
import { AGENT_BROWSER_PARAMS
|
|
15
|
+
import { AGENT_BROWSER_PARAMS } from "./lib/input-modes/params.js";
|
|
23
16
|
import { parseAllowedDomainsPolicyFromArgs } from "./lib/navigation-policy.js";
|
|
24
17
|
import { closeManagedSession, getSessionContextKey, runAgentBrowserTool } from "./lib/orchestration/browser-run/index.js";
|
|
25
18
|
import { findElectronLaunchRecordForSession, getActiveElectronRecords } from "./lib/orchestration/browser-run/session-state.js";
|
|
@@ -112,6 +105,57 @@ function restoreManagedSessionCompatibilityWorkaroundFromBranch(branch, sessionN
|
|
|
112
105
|
}
|
|
113
106
|
return restored;
|
|
114
107
|
}
|
|
108
|
+
function restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, sessionName, namespace) {
|
|
109
|
+
let restored = false;
|
|
110
|
+
const targetKey = getSessionContextKey(sessionName, namespace);
|
|
111
|
+
for (const entry of branch) {
|
|
112
|
+
if (!isRecord(entry) || entry.type !== "message")
|
|
113
|
+
continue;
|
|
114
|
+
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
115
|
+
if (!message || message.toolName !== "agent_browser")
|
|
116
|
+
continue;
|
|
117
|
+
const details = isRecord(message.details) ? message.details : undefined;
|
|
118
|
+
if (!details)
|
|
119
|
+
continue;
|
|
120
|
+
if (getSessionContextKey(typeof details.sessionName === "string" ? details.sessionName : undefined, typeof details.namespace === "string" ? details.namespace : undefined) !== targetKey)
|
|
121
|
+
continue;
|
|
122
|
+
const outcome = getManagedSessionOutcome(details);
|
|
123
|
+
const activeAfterFailure = outcome?.activeAfter === true
|
|
124
|
+
&& typeof outcome.currentSessionName === "string"
|
|
125
|
+
&& getSessionContextKey(outcome.currentSessionName, typeof outcome.currentSessionNamespace === "string" ? outcome.currentSessionNamespace : undefined) === targetKey;
|
|
126
|
+
if ((getSuccessfulToolResult(details, message) || activeAfterFailure) && typeof details.managedSessionHeadedAutosaveDisabled === "boolean") {
|
|
127
|
+
restored = details.managedSessionHeadedAutosaveDisabled;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return restored;
|
|
131
|
+
}
|
|
132
|
+
function restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, sessionName, namespace) {
|
|
133
|
+
let restored;
|
|
134
|
+
const targetKey = getSessionContextKey(sessionName, namespace);
|
|
135
|
+
for (const entry of branch) {
|
|
136
|
+
if (!isRecord(entry) || entry.type !== "message")
|
|
137
|
+
continue;
|
|
138
|
+
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
139
|
+
if (!message || message.toolName !== "agent_browser")
|
|
140
|
+
continue;
|
|
141
|
+
const details = isRecord(message.details) ? message.details : undefined;
|
|
142
|
+
if (!details)
|
|
143
|
+
continue;
|
|
144
|
+
if (getSessionContextKey(typeof details.sessionName === "string" ? details.sessionName : undefined, typeof details.namespace === "string" ? details.namespace : undefined) !== targetKey)
|
|
145
|
+
continue;
|
|
146
|
+
const outcome = getManagedSessionOutcome(details);
|
|
147
|
+
const activeAfterFailure = outcome?.activeAfter === true
|
|
148
|
+
&& typeof outcome.currentSessionName === "string"
|
|
149
|
+
&& getSessionContextKey(outcome.currentSessionName, typeof outcome.currentSessionNamespace === "string" ? outcome.currentSessionNamespace : undefined) === targetKey;
|
|
150
|
+
if (!getSuccessfulToolResult(details, message) && !activeAfterFailure)
|
|
151
|
+
continue;
|
|
152
|
+
if (typeof details.managedSessionHeadedAutosaveInterval === "string")
|
|
153
|
+
restored = details.managedSessionHeadedAutosaveInterval;
|
|
154
|
+
else if (details.managedSessionHeadedAutosaveDisabled === true)
|
|
155
|
+
restored = "0";
|
|
156
|
+
}
|
|
157
|
+
return restored;
|
|
158
|
+
}
|
|
115
159
|
function getToolResultArgs(details) {
|
|
116
160
|
if (Array.isArray(details.args) && details.args.every((arg) => typeof arg === "string"))
|
|
117
161
|
return details.args;
|
|
@@ -125,7 +169,7 @@ function isAttachedBrowserInvocation(args, env = process.env) {
|
|
|
125
169
|
|| hasLaunchScopedFlagToken(args, "--cdp")
|
|
126
170
|
|| hasLaunchScopedFlagToken(args, "--auto-connect")
|
|
127
171
|
|| env.AGENT_BROWSER_CDP !== undefined
|
|
128
|
-
|| (autoConnectEnv
|
|
172
|
+
|| isUpstreamEnvFlagEnabled(autoConnectEnv);
|
|
129
173
|
}
|
|
130
174
|
function restoreAttachedSessionKeysFromBranch(branch) {
|
|
131
175
|
const attachedSessionKeys = new Set();
|
|
@@ -216,7 +260,9 @@ function trackOwnedManagedSession(sessions, sessionName, cwd, options = {}) {
|
|
|
216
260
|
const key = getSessionContextKey(sessionName, options.namespace) ?? sessionName;
|
|
217
261
|
const existing = sessions.get(key);
|
|
218
262
|
const branchOwned = existing && !existing.branchOwned ? false : options.branchOwned === true;
|
|
219
|
-
|
|
263
|
+
const headedManagedAutosaveDisabled = options.headedManagedAutosaveDisabled ?? existing?.headedManagedAutosaveDisabled;
|
|
264
|
+
const headedManagedAutosaveInterval = options.headedManagedAutosaveInterval ?? existing?.headedManagedAutosaveInterval;
|
|
265
|
+
sessions.set(key, { branchOwned, cwd, headedManagedAutosaveDisabled, headedManagedAutosaveInterval, namespace: options.namespace, sessionName });
|
|
220
266
|
}
|
|
221
267
|
function untrackOwnedManagedSession(sessions, sessionName, namespace) {
|
|
222
268
|
if (!sessionName)
|
|
@@ -247,7 +293,11 @@ function syncOwnedManagedSessionsFromResult(sessions, result, cwd) {
|
|
|
247
293
|
const attemptedSessionName = typeof outcome.attemptedSessionName === "string" ? outcome.attemptedSessionName : undefined;
|
|
248
294
|
if (outcome.activeAfter === true && (status === "created" || status === "replaced" || status === "unchanged")) {
|
|
249
295
|
const namespace = isRecord(details) && typeof details.namespace === "string" ? details.namespace : undefined;
|
|
250
|
-
trackOwnedManagedSession(sessions, currentSessionName, cwd, {
|
|
296
|
+
trackOwnedManagedSession(sessions, currentSessionName, cwd, {
|
|
297
|
+
headedManagedAutosaveDisabled: details?.managedSessionHeadedAutosaveDisabled === true,
|
|
298
|
+
headedManagedAutosaveInterval: typeof details?.managedSessionHeadedAutosaveInterval === "string" ? details.managedSessionHeadedAutosaveInterval : undefined,
|
|
299
|
+
namespace,
|
|
300
|
+
});
|
|
251
301
|
}
|
|
252
302
|
if (succeeded && status === "closed") {
|
|
253
303
|
untrackOwnedManagedSession(sessions, attemptedSessionName ?? currentSessionName);
|
|
@@ -382,10 +432,18 @@ function setBranchRankForString(map, value, rank) {
|
|
|
382
432
|
if (typeof value === "string" && value.length > 0)
|
|
383
433
|
map.set(value, rank);
|
|
384
434
|
}
|
|
435
|
+
function setBranchManagedSessionActive(events, sessionName, namespace, rank) {
|
|
436
|
+
if (typeof sessionName !== "string" || sessionName.length === 0)
|
|
437
|
+
return;
|
|
438
|
+
const key = getSessionContextKey(sessionName, namespace) ?? sessionName;
|
|
439
|
+
events.managedSessionActiveIdentities.set(key, { namespace, sessionName });
|
|
440
|
+
events.managedSessionActiveRanks.set(key, rank);
|
|
441
|
+
}
|
|
385
442
|
function collectBranchManagedResourceEvents(branch) {
|
|
386
443
|
const events = {
|
|
387
444
|
electronLaunchActiveRanks: new Map(),
|
|
388
445
|
electronLaunchCleanupRanks: new Map(),
|
|
446
|
+
managedSessionActiveIdentities: new Map(),
|
|
389
447
|
managedSessionActiveRanks: new Map(),
|
|
390
448
|
managedSessionCloseRanks: new Map(),
|
|
391
449
|
};
|
|
@@ -414,17 +472,17 @@ function collectBranchManagedResourceEvents(branch) {
|
|
|
414
472
|
const outcomeCurrentSessionName = typeof outcome?.currentSessionName === "string" ? outcome.currentSessionName : undefined;
|
|
415
473
|
const outcomeAttemptedSessionName = typeof outcome?.attemptedSessionName === "string" ? outcome.attemptedSessionName : undefined;
|
|
416
474
|
if (outcomeSucceeded && outcome.activeAfter === true && (outcomeStatus === "created" || outcomeStatus === "replaced" || outcomeStatus === "unchanged")) {
|
|
417
|
-
|
|
475
|
+
setBranchManagedSessionActive(events, outcomeCurrentSessionName, namespace, eventRank);
|
|
418
476
|
}
|
|
419
477
|
if (outcomeSucceeded && outcomeStatus === "closed") {
|
|
420
478
|
setBranchRankForString(events.managedSessionCloseRanks, getSessionContextKey(outcomeAttemptedSessionName ?? outcomeCurrentSessionName ?? sessionName, namespace), eventRank);
|
|
421
479
|
}
|
|
422
|
-
if (
|
|
480
|
+
if (outcome && outcomeStatus === "replaced" && outcome.replacedSessionClosed !== false) {
|
|
423
481
|
const replacedSessionNamespace = typeof outcome.replacedSessionNamespace === "string" ? outcome.replacedSessionNamespace : namespace;
|
|
424
482
|
setBranchRankForString(events.managedSessionCloseRanks, getSessionContextKey(typeof outcome.replacedSessionName === "string" ? outcome.replacedSessionName : undefined, replacedSessionNamespace), eventRank);
|
|
425
483
|
}
|
|
426
|
-
if (succeeded && !isCloseCommand(command) && sessionName && (usedImplicitSession || sessionMode === "fresh")) {
|
|
427
|
-
events
|
|
484
|
+
if (succeeded && !isCloseCommand(command) && sessionName && (usedImplicitSession || sessionMode === "fresh" || details.managedSessionHeadedAutosaveDisabled === true || typeof details.managedSessionHeadedAutosaveInterval === "string")) {
|
|
485
|
+
setBranchManagedSessionActive(events, sessionName, namespace, eventRank);
|
|
428
486
|
}
|
|
429
487
|
if (succeeded && isCloseCommand(command)) {
|
|
430
488
|
setBranchRankForString(events.managedSessionCloseRanks, getSessionContextKey(explicitSessionName ?? sessionName ?? outcomeAttemptedSessionName ?? outcomeCurrentSessionName, namespace), eventRank);
|
|
@@ -475,7 +533,7 @@ async function closeOwnedManagedSessionsExcept(sessions, restoreState, keepSessi
|
|
|
475
533
|
for (const [key, owner] of [...sessions]) {
|
|
476
534
|
if (key === keepKey)
|
|
477
535
|
continue;
|
|
478
|
-
const error = await closeManagedSession({ cwd: owner.cwd, namespace: owner.namespace, preserveAttachedBrowserSession: attachedSessionKeys.has(key), restoreState, sessionName: owner.sessionName, timeoutMs });
|
|
536
|
+
const error = await closeManagedSession({ cwd: owner.cwd, headedManagedAutosaveInterval: owner.headedManagedAutosaveInterval, namespace: owner.namespace, preserveAttachedBrowserSession: attachedSessionKeys.has(key), restoreState, sessionName: owner.sessionName, timeoutMs });
|
|
479
537
|
if (!error)
|
|
480
538
|
sessions.delete(key);
|
|
481
539
|
}
|
|
@@ -614,6 +672,8 @@ export default function agentBrowserExtension(pi) {
|
|
|
614
672
|
let managedSessionActive = false;
|
|
615
673
|
let managedSessionBaseName = createImplicitSessionName(undefined, process.cwd(), ephemeralSessionSeed);
|
|
616
674
|
let managedSessionCompatibilityWorkaround;
|
|
675
|
+
let managedSessionHeadedAutosaveDisabled = false;
|
|
676
|
+
let managedSessionHeadedAutosaveInterval;
|
|
617
677
|
let managedSessionName = managedSessionBaseName;
|
|
618
678
|
let managedSessionCwd = process.cwd();
|
|
619
679
|
let managedSessionNamespace;
|
|
@@ -680,6 +740,11 @@ export default function agentBrowserExtension(pi) {
|
|
|
680
740
|
managedSessionCompatibilityWorkaround = managedSessionActive
|
|
681
741
|
? restoreManagedSessionCompatibilityWorkaroundFromBranch(branch, managedSessionName, managedSessionNamespace)
|
|
682
742
|
: undefined;
|
|
743
|
+
managedSessionHeadedAutosaveDisabled = managedSessionActive
|
|
744
|
+
&& restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, managedSessionName, managedSessionNamespace);
|
|
745
|
+
managedSessionHeadedAutosaveInterval = managedSessionActive
|
|
746
|
+
? restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, managedSessionName, managedSessionNamespace)
|
|
747
|
+
: undefined;
|
|
683
748
|
managedSessionCwd = ctx.cwd;
|
|
684
749
|
freshSessionOrdinal = nextFreshSessionOrdinal;
|
|
685
750
|
sessionPageState = SessionPageState.fromBranch(branch);
|
|
@@ -704,8 +769,41 @@ export default function agentBrowserExtension(pi) {
|
|
|
704
769
|
}
|
|
705
770
|
removeInactiveOwnedElectronLaunchRecords(ownedElectronLaunchRecords, branchOwnedElectronLaunchIds, electronLaunchRecords, branchResourceEvents.electronLaunchActiveRanks, branchResourceEvents.electronLaunchCleanupRanks);
|
|
706
771
|
}
|
|
772
|
+
for (const [sessionKey, identity] of branchResourceEvents.managedSessionActiveIdentities) {
|
|
773
|
+
const activeRank = branchResourceEvents.managedSessionActiveRanks.get(sessionKey);
|
|
774
|
+
const closeRank = branchResourceEvents.managedSessionCloseRanks.get(sessionKey);
|
|
775
|
+
if (activeRank === undefined || (closeRank !== undefined && closeRank >= activeRank))
|
|
776
|
+
continue;
|
|
777
|
+
if (!isRestorableManagedSessionName(identity.sessionName, managedSessionBaseName))
|
|
778
|
+
continue;
|
|
779
|
+
trackOwnedManagedSession(ownedManagedSessions, identity.sessionName, ctx.cwd, {
|
|
780
|
+
branchOwned: true,
|
|
781
|
+
headedManagedAutosaveDisabled: restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, identity.sessionName, identity.namespace),
|
|
782
|
+
headedManagedAutosaveInterval: restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, identity.sessionName, identity.namespace),
|
|
783
|
+
namespace: identity.namespace,
|
|
784
|
+
});
|
|
785
|
+
}
|
|
707
786
|
if (restoredState.active) {
|
|
708
|
-
trackOwnedManagedSession(ownedManagedSessions, restoredState.sessionName, ctx.cwd, {
|
|
787
|
+
trackOwnedManagedSession(ownedManagedSessions, restoredState.sessionName, ctx.cwd, {
|
|
788
|
+
branchOwned: true,
|
|
789
|
+
headedManagedAutosaveDisabled: managedSessionHeadedAutosaveDisabled,
|
|
790
|
+
headedManagedAutosaveInterval: managedSessionHeadedAutosaveInterval,
|
|
791
|
+
namespace: restoredState.namespace,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
for (const record of getActiveElectronRecords(electronLaunchRecords)) {
|
|
795
|
+
if (!record.sessionName || !isRestorableManagedSessionName(record.sessionName, managedSessionBaseName))
|
|
796
|
+
continue;
|
|
797
|
+
const sessionKey = getSessionContextKey(record.sessionName) ?? record.sessionName;
|
|
798
|
+
const activeRank = branchResourceEvents.managedSessionActiveRanks.get(sessionKey);
|
|
799
|
+
const closeRank = branchResourceEvents.managedSessionCloseRanks.get(sessionKey);
|
|
800
|
+
if (activeRank === undefined || (closeRank !== undefined && closeRank >= activeRank))
|
|
801
|
+
continue;
|
|
802
|
+
trackOwnedManagedSession(ownedManagedSessions, record.sessionName, ctx.cwd, {
|
|
803
|
+
branchOwned: true,
|
|
804
|
+
headedManagedAutosaveDisabled: restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, record.sessionName),
|
|
805
|
+
headedManagedAutosaveInterval: restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, record.sessionName),
|
|
806
|
+
});
|
|
709
807
|
}
|
|
710
808
|
mergeActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronLaunchRecords, {
|
|
711
809
|
branchOwnedLaunchIds: branchOwnedElectronLaunchIds,
|
|
@@ -768,6 +866,7 @@ export default function agentBrowserExtension(pi) {
|
|
|
768
866
|
electronChildProcesses,
|
|
769
867
|
electronLaunchRecords: electronRecordsToCleanup,
|
|
770
868
|
managedSessionRestoreState,
|
|
869
|
+
ownedManagedSessions,
|
|
771
870
|
timeoutMs: implicitSessionCloseTimeoutMs,
|
|
772
871
|
});
|
|
773
872
|
preservedElectronProfileDirs = [...new Set([
|
|
@@ -784,6 +883,8 @@ export default function agentBrowserExtension(pi) {
|
|
|
784
883
|
});
|
|
785
884
|
managedSessionActive = false;
|
|
786
885
|
managedSessionCompatibilityWorkaround = undefined;
|
|
886
|
+
managedSessionHeadedAutosaveDisabled = false;
|
|
887
|
+
managedSessionHeadedAutosaveInterval = undefined;
|
|
787
888
|
managedSessionNamespace = undefined;
|
|
788
889
|
sessionPageState.reset();
|
|
789
890
|
traceOwners = new Map();
|
|
@@ -890,6 +991,7 @@ export default function agentBrowserExtension(pi) {
|
|
|
890
991
|
managedSessionName,
|
|
891
992
|
managedSessionNamespace,
|
|
892
993
|
managedSessionRestoreState,
|
|
994
|
+
ownedManagedSessions,
|
|
893
995
|
redactedCompiledElectron,
|
|
894
996
|
sessionPageState,
|
|
895
997
|
signal,
|
|
@@ -917,6 +1019,8 @@ export default function agentBrowserExtension(pi) {
|
|
|
917
1019
|
if (closedSessionName === managedSessionName) {
|
|
918
1020
|
managedSessionActive = false;
|
|
919
1021
|
managedSessionCompatibilityWorkaround = undefined;
|
|
1022
|
+
managedSessionHeadedAutosaveDisabled = false;
|
|
1023
|
+
managedSessionHeadedAutosaveInterval = undefined;
|
|
920
1024
|
managedSessionNamespace = undefined;
|
|
921
1025
|
freshSessionOrdinal += 1;
|
|
922
1026
|
managedSessionName = createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal);
|
|
@@ -959,6 +1063,8 @@ export default function agentBrowserExtension(pi) {
|
|
|
959
1063
|
managedSessionActive,
|
|
960
1064
|
managedSessionBaseName,
|
|
961
1065
|
managedSessionCompatibilityWorkaround,
|
|
1066
|
+
managedSessionHeadedAutosaveDisabled,
|
|
1067
|
+
managedSessionHeadedAutosaveInterval,
|
|
962
1068
|
managedSessionCwd,
|
|
963
1069
|
managedSessionName,
|
|
964
1070
|
managedSessionNamespace,
|
|
@@ -1037,6 +1143,8 @@ export default function agentBrowserExtension(pi) {
|
|
|
1037
1143
|
freshSessionOrdinal = Math.max(freshSessionOrdinal, browserRunState.freshSessionOrdinal);
|
|
1038
1144
|
managedSessionActive = browserRunState.managedSessionActive;
|
|
1039
1145
|
managedSessionCompatibilityWorkaround = browserRunState.managedSessionCompatibilityWorkaround;
|
|
1146
|
+
managedSessionHeadedAutosaveDisabled = browserRunState.managedSessionHeadedAutosaveDisabled === true;
|
|
1147
|
+
managedSessionHeadedAutosaveInterval = browserRunState.managedSessionHeadedAutosaveInterval;
|
|
1040
1148
|
managedSessionCwd = browserRunState.managedSessionCwd;
|
|
1041
1149
|
managedSessionName = browserRunState.managedSessionName;
|
|
1042
1150
|
managedSessionNamespace = browserRunState.managedSessionNamespace;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Parse raw agent-browser argv once into a stable command descriptor for planners and policy.
|
|
3
|
-
* Responsibilities: Own command-token extraction, command/subcommand identification, and descriptor construction.
|
|
4
|
-
* Scope: Pure argv parsing; runtime planning and session policy consume descriptors instead of re-parsing tokens.
|
|
5
|
-
*/
|
|
6
1
|
import { GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, VALUE_FLAGS, optionalGlobalValueFlagConsumesNext } from "./argv-grammar.js";
|
|
7
2
|
import { isOpenNavigationCommand } from "./command-taxonomy.js";
|
|
8
3
|
function isBooleanLiteral(token) {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Shared argv flag-shape metadata and helpers for command discovery and sessionless policy checks.
|
|
3
|
-
* Responsibilities: Own global/command value-flag sets and boolean/value-flag validation used during argv parsing.
|
|
4
|
-
* Scope: Pure token grammar; command semantics and subprocess execution live elsewhere.
|
|
5
|
-
*/
|
|
6
1
|
import { isKnownCommandToken } from "./command-taxonomy.js";
|
|
7
2
|
export const GLOBAL_VALUE_FLAGS = [
|
|
8
3
|
"--session",
|
|
@@ -125,6 +120,10 @@ export function getBooleanFlagValue(args, flag) {
|
|
|
125
120
|
export function isBooleanFlagEnabled(args, flag) {
|
|
126
121
|
return getBooleanFlagValue(args, flag) ?? false;
|
|
127
122
|
}
|
|
123
|
+
/** Match upstream env_var_is_truthy exactly: lowercase only, without trimming or accepting "off". */
|
|
124
|
+
export function isUpstreamEnvFlagEnabled(value) {
|
|
125
|
+
return value !== undefined && !["", "0", "false", "no"].includes(value.toLowerCase());
|
|
126
|
+
}
|
|
128
127
|
/** Mirror upstream sanitize_session_component for namespace/socket/state identity. */
|
|
129
128
|
export function canonicalizeAgentBrowserNamespace(value) {
|
|
130
129
|
if (value === undefined)
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Own upstream command-shape policies that decide whether the wrapper should allocate a managed browser session.
|
|
3
|
-
* Responsibilities: Keep local/sessionless command grammar out of the runtime execution planner while preserving exact upstream shapes.
|
|
4
|
-
* Scope: Pure argv-token policy; command discovery, subprocess execution, and presentation live in focused modules.
|
|
5
|
-
*/
|
|
6
1
|
import { hasOnlyBooleanFlags, hasOnlyOptionFlags, isNonFlagToken, stripSessionlessShapeGlobalFlags } from "./argv-grammar.js";
|
|
7
2
|
const SESSIONLESS_AUTH_SUBCOMMANDS = new Set(["save", "list", "show", "delete", "remove"]);
|
|
8
3
|
const PLUGIN_SESSIONLESS_SUBCOMMANDS = new Set(["list", "show", "add", "run"]);
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Centralize upstream agent-browser command capabilities that wrapper behavior depends on.
|
|
3
|
-
* Responsibilities: Normalize command aliases once and expose capability predicates for runtime planning,
|
|
4
|
-
* session/ref guards, result recommendations, and presentation summaries without coupling unrelated behaviors.
|
|
5
|
-
* Scope: Static command capability taxonomy only; command-shape parsing, spawning, and formatting live elsewhere.
|
|
6
|
-
*/
|
|
7
1
|
const ADDITIONAL_COMMAND_TOKENS = [
|
|
8
2
|
"a11y", "auth", "chat", "clipboard", "confirm", "connect", "dashboard", "deny", "device", "dialog", "diff", "doctor", "errors", "eval", "find", "frame", "get", "highlight", "inspect", "install", "is", "mcp", "plugin", "plugins", "profiles", "profiler", "react", "record", "removeinitscript", "session", "set", "skills", "snapshot", "state", "stream", "trace", "upgrade", "vitals", "wait", "web-vitals", "window",
|
|
9
3
|
];
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
/**
|
|
3
|
-
* Purpose: Canonical pi-agent-browser-native config policy shared by runtime and setup CLI.
|
|
4
|
-
* Responsibilities: Own config paths, provider descriptors, credential source parsing, layer validation/merge, status projection, and redacted summaries.
|
|
5
|
-
* Scope: Pure configuration policy plus synchronous status loading; secret command execution and browser/web-search runtime calls live elsewhere.
|
|
6
|
-
*/
|
|
7
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
3
|
import { homedir } from "node:os";
|
|
9
4
|
import { join, resolve } from "node:path";
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Load pi-agent-browser-native package configuration from Pi-scoped global, project, or explicit paths.
|
|
3
|
-
* Responsibilities: Resolve config layers, resolve secrets without exposing values, and provide redacted status for tools/CLIs.
|
|
4
|
-
* Scope: Package-owned configuration only; canonical config policy lives in config-policy.js, browser command execution and web-search API calls live in focused modules.
|
|
5
|
-
* Invariants/Assumptions: Credential sources from loaded config are passed through to the runtime; command credentials are resolved lazily at execution time and displayed values stay redacted.
|
|
6
|
-
*/
|
|
7
1
|
import { exec as execCallback } from "node:child_process";
|
|
8
2
|
import { readFile } from "node:fs/promises";
|
|
9
3
|
import { promisify } from "node:util";
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Parse and fetch Chrome DevTools Protocol metadata for wrapper-owned Electron launches.
|
|
3
|
-
* Responsibilities: Normalize CDP version/target JSON and perform bounded localhost CDP JSON fetches.
|
|
4
|
-
* Scope: Tiny Electron CDP boundary helpers only; launch, status, cleanup, and target selection stay in their owning modules.
|
|
5
|
-
* Usage: Imported by Electron launch and cleanup paths when polling `/json/version` and `/json/list`.
|
|
6
|
-
* Invariants/Assumptions: Malformed or unavailable CDP endpoints return undefined/empty metadata rather than throwing, matching prior caller behavior.
|
|
7
|
-
*/
|
|
8
1
|
import { isRecord } from "../parsing.js";
|
|
9
2
|
const ELECTRON_CDP_FETCH_TIMEOUT_MS = 1_000;
|
|
10
3
|
function asString(value) {
|
|
@@ -49,3 +42,9 @@ export async function fetchCdpJson(url, signal) {
|
|
|
49
42
|
clearTimeout(timeout);
|
|
50
43
|
}
|
|
51
44
|
}
|
|
45
|
+
export function boundElectronProbeString(value, maxLength = 240) {
|
|
46
|
+
const trimmed = value?.trim();
|
|
47
|
+
if (!trimmed)
|
|
48
|
+
return undefined;
|
|
49
|
+
return trimmed.length > maxLength ? `${trimmed.slice(0, Math.max(0, maxLength - 3))}...` : trimmed;
|
|
50
|
+
}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Inspect and clean wrapper-owned Electron launch resources.
|
|
3
|
-
* Responsibilities: Report CDP liveness/targets without mutation and remove only tracked process/profile resources during explicit or shutdown cleanup.
|
|
4
|
-
* Scope: Host-side Electron status and resource cleanup only; upstream managed-session close remains in the extension entrypoint.
|
|
5
|
-
* Usage: Called by electron.status, electron.cleanup, and session_shutdown handling.
|
|
6
|
-
* Invariants/Assumptions: Cleanup operates only on launch records produced by this wrapper and prefers partial cleanup reporting over killing or deleting untracked resources.
|
|
7
|
-
*/
|
|
8
1
|
import { execFile } from "node:child_process";
|
|
9
2
|
import { rm } from "node:fs/promises";
|
|
10
3
|
import { promisify } from "node:util";
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Discover installed Electron desktop applications without invoking upstream agent-browser.
|
|
3
|
-
* Responsibilities: Scan bounded macOS app bundles and Linux .desktop launchers, apply Electron framework evidence gates, and return small platform-tagged app metadata.
|
|
4
|
-
* Scope: Discovery only; launch, cleanup, status, and CDP attachment live in later Electron lifecycle work items.
|
|
5
|
-
* Usage: Called by the agent_browser top-level electron.list shorthand and directly by tests through parameterized scan locations.
|
|
6
|
-
* Invariants/Assumptions: Discovery is best-effort, missing scan roots are ignored, malformed .desktop files are skipped, and results are capped before they reach model-visible output.
|
|
7
|
-
*/
|
|
8
1
|
import { constants as fsConstants } from "node:fs";
|
|
9
2
|
import { access, readdir, readFile, realpath, stat } from "node:fs/promises";
|
|
10
3
|
import { homedir } from "node:os";
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Launch wrapper-owned Electron applications and discover their CDP endpoint.
|
|
3
|
-
* Responsibilities: Resolve Electron targets, enforce caller-owned allow/deny policy, create isolated userDataDir profiles, launch with remote debugging on an OS-chosen port, poll DevToolsActivePort, and read bounded CDP version/target metadata.
|
|
4
|
-
* Scope: Host-side Electron lifecycle setup only; upstream agent-browser attach/presentation stays in the extension entrypoint.
|
|
5
|
-
* Usage: Called by the agent_browser electron.launch shorthand before routing through upstream `connect`.
|
|
6
|
-
* Invariants/Assumptions: The wrapper only launches targets with Electron framework evidence, always uses an isolated temp profile, never accepts a caller-supplied remote debugging port, and cleans any spawned process/profile when cancellation interrupts readiness.
|
|
7
|
-
*/
|
|
8
1
|
import { spawn } from "node:child_process";
|
|
9
2
|
import { randomUUID } from "node:crypto";
|
|
10
3
|
import { readFile, rm } from "node:fs/promises";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Compile top-level Electron wrapper inputs into validated Electron actions.
|
|
3
|
-
* Responsibilities: Enforce action-specific fields, launch-target rules, and wrapper-owned flag safety.
|
|
4
|
-
* Scope: Electron input-mode validation only; launch/probe/cleanup execution stays in the extension entrypoint.
|
|
5
|
-
*/
|
|
6
1
|
import { isRecord } from "../parsing.js";
|
|
7
2
|
import { AGENT_BROWSER_ELECTRON_ACTIONS, AGENT_BROWSER_ELECTRON_HANDOFFS, AGENT_BROWSER_ELECTRON_LIST_FIELDS, AGENT_BROWSER_ELECTRON_PROBE_FIELDS, AGENT_BROWSER_ELECTRON_RESERVED_APP_ARGS, AGENT_BROWSER_ELECTRON_TARGET_TYPES, } from "./types.js";
|
|
8
3
|
function validateOptionalNonEmptyString(input, fieldName) {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Compile constrained job and lightweight QA wrapper inputs to upstream batch commands.
|
|
3
|
-
* Responsibilities: Validate job/QA fields, produce argv/stdin, and summarize QA diagnostic results.
|
|
4
|
-
* Scope: Job and QA modes only.
|
|
5
|
-
*/
|
|
6
1
|
import { isRecord } from "../parsing.js";
|
|
7
2
|
import { summarizeNetworkFailures } from "../results/network.js";
|
|
8
3
|
import { getBatchResultItems, getCommandNameFromBatchItem, getSelectValues } from "./shared.js";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Compile and analyze experimental DOM/source and network source lookup input modes.
|
|
3
|
-
* Responsibilities: Build lookup batches, collect DOM/React/workspace candidates, and redact network lookup surfaces.
|
|
4
|
-
* Scope: Source lookup and network source lookup only.
|
|
5
|
-
*/
|
|
6
1
|
import { readFile, readdir } from "node:fs/promises";
|
|
7
2
|
import { extname, join } from "node:path";
|
|
8
3
|
import { isRecord } from "../parsing.js";
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Define the Pi tool input schema for the native agent_browser wrapper.
|
|
3
|
-
* Responsibilities: Keep TypeBox schema construction separate from runtime execution and input-mode compilers.
|
|
4
|
-
* Scope: Schema-only; behavioral validation lives in the mode compilers.
|
|
5
|
-
*/
|
|
6
1
|
import { JsonSchema } from "../json-schema.js";
|
|
7
2
|
import { StringEnum as localStringEnum } from "../string-enum-schema.js";
|
|
8
3
|
import { ELECTRON_DISCOVERY_DEFAULT_MAX_RESULTS, ELECTRON_DISCOVERY_MAX_RESULTS, } from "../electron/discovery.js";
|
|
@@ -128,7 +123,7 @@ export function createAgentBrowserParamsSchema(Type = JsonSchema, StringEnum = l
|
|
|
128
123
|
}, { additionalProperties: false }), { minItems: 1 }),
|
|
129
124
|
}, { additionalProperties: false, description: "Constrained multi-step batch." })),
|
|
130
125
|
stdin: Type.Optional(Type.String({ description: "Raw stdin for batch, eval --stdin, or auth save --password-stdin; unavailable with structured modes and electron." })),
|
|
131
|
-
outputPath: Type.Optional(Type.String({ description: "Workspace-relative or absolute result path.", minLength: 1 })),
|
|
126
|
+
outputPath: Type.Optional(Type.String({ description: "Workspace-relative or absolute result-data path; keep it distinct from screenshot, download, recording, and other browser artifact destinations.", minLength: 1 })),
|
|
132
127
|
timeoutMs: Type.Optional(Type.Integer({ description: "Wrapper timeout in ms; exceed explicit waits. Electron uses electron.timeoutMs.", minimum: 1 })),
|
|
133
128
|
sessionMode: Type.Optional(StringEnum(["auto", "fresh"], {
|
|
134
129
|
description: "auto reuses the managed session; fresh starts one for launch-only flags, then makes it the managed session.",
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Compile semanticAction shorthand inputs into upstream agent-browser commands.
|
|
3
|
-
* Responsibilities: Validate shorthand locator/action fields and expose compiled-command helpers.
|
|
4
|
-
* Scope: semanticAction mode only.
|
|
5
|
-
*/
|
|
6
1
|
import { isRecord } from "../parsing.js";
|
|
7
2
|
import { getSelectValues } from "./shared.js";
|
|
8
3
|
import { AGENT_BROWSER_SEMANTIC_ACTIONS, AGENT_BROWSER_SEMANTIC_LOCATORS, } from "./types.js";
|