pi-lean-portal 0.4.0 → 0.6.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 (44) hide show
  1. package/README.md +156 -491
  2. package/backends/chromium/index.ts +1 -1
  3. package/backends/firefox/index.ts +1 -1
  4. package/backends/playwright-base/playwright-plugin.ts +41 -108
  5. package/backends/python-adapter.ts +40 -66
  6. package/backends/python-base/pi_browser_bridge/accessibility.py +30 -15
  7. package/backends/python-base/pi_browser_bridge/browser_data.py +34 -42
  8. package/backends/python-base/pi_browser_bridge/playwright_base.py +68 -87
  9. package/backends/python-base/pi_browser_bridge/transport.py +6 -21
  10. package/browser-cookies.ts +5 -0
  11. package/browser-install.ts +454 -0
  12. package/browser-profile.ts +7 -13
  13. package/browser-status.ts +21 -0
  14. package/browser-toggle.ts +35 -76
  15. package/core/fetch-backend.ts +22 -71
  16. package/core/guides.ts +134 -23
  17. package/core/plugin-api.ts +14 -26
  18. package/core/plugin-config.ts +38 -12
  19. package/core/plugin-registry.ts +7 -2
  20. package/core/router.ts +139 -186
  21. package/core/shared/accessibility-tree.ts +7 -2
  22. package/core/shared/bot-detection.ts +7 -7
  23. package/core/shared/browser-events.ts +10 -14
  24. package/core/shared/dom-extractor.ts +4 -18
  25. package/core/shared/nav-settle.ts +6 -6
  26. package/core/shared/session-manager.ts +7 -35
  27. package/core/shared/settings-reader.ts +5 -3
  28. package/core/shared/snapshot-cache.ts +27 -70
  29. package/core/shared/storage-state.ts +13 -12
  30. package/core/shared/temp-files.ts +73 -0
  31. package/index.ts +30 -66
  32. package/package.json +5 -6
  33. package/tools/browser-back.ts +8 -3
  34. package/tools/browser-console.ts +3 -8
  35. package/tools/browser-inspect.ts +15 -14
  36. package/tools/browser-navigate.ts +47 -26
  37. package/tools/browser-press.ts +7 -2
  38. package/tools/browser-scroll.ts +8 -3
  39. package/tools/browser-snapshot.ts +10 -8
  40. package/tools/index.ts +4 -1
  41. package/tools/utils.ts +22 -9
  42. package/tools/web-fetch.ts +12 -8
  43. package/core/shared/ship-manifest.ts +0 -151
  44. package/ship-manifest.test.ts +0 -17
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  # pi-lean-portal User Guide
2
2
 
3
- > **pi-lean-portal** gives the Pi coding agent interactive web browsing —
4
- > Playwright Chromium/Firefox, accessibility-tree snapshots with `@e` element
5
- > refs, persistent profiles, cookies, and navigation guides that resurface by
6
- > domain. A `/web` toggle removes the tools from the agent's context when
3
+ > **pi-lean-portal** gives the Pi coding agent interactive web browsing via Playwright Chromium/Firefox, accessibility-tree snapshots with `@e` element
4
+ > refs, persistent profiles, cookies, and domain-aware navigation guides. A `/web` toggle removes the tools from the agent's context when
7
5
  > switched off, so web browsing doesn't consume tokens on sessions that aren't
8
6
  > doing web work. If a site blocks the shipped browsers, drop in your own
9
7
  > backend (e.g. [Camoufox](https://github.com/daijro/camoufox)).
@@ -14,71 +12,21 @@
14
12
 
15
13
  ---
16
14
 
17
- ## Table of Contents
18
-
19
- 1. [Quick Start](#quick-start)
20
- 2. [Extending it](#extending-it)
21
- 3. [`/web` Command — Browser Toggle & Profiles](#web-command--browser-toggle--profiles)
22
- 4. [All 12 Tools](#all-12-tools)
23
- 5. [Stateless Fetching (web-fetch)](#stateless-fetching-web-fetch)
24
- 6. [Navigation Guides (web-guide & web-learn)](#navigation-guides-web-guide--web-learn)
25
- 7. [`/web status` — Detailed Runtime Status](#web-status--detailed-runtime-status)
26
- 8. [Profiles — Persistent Sessions](#profiles--persistent-sessions)
27
- 9. [Cookie Management](#cookie-management)
28
- 10. [Backend Architecture](#backend-architecture)
29
- 11. [Configuration (settings.json)](#configuration-settingsjson)
30
- 12. [Tips & Best Practices](#tips--best-practices)
31
-
32
- ---
33
-
34
15
  ## Quick Start
35
16
 
36
17
  ```bash
37
18
  pi install npm:pi-lean-portal
38
- npx playwright install chromium firefox
39
19
  ```
40
20
 
21
+ Then run **`/web install`** inside pi to download browser binaries (these are not installed via npm).
22
+
41
23
  Once loaded, you'll see a notification like:
42
24
 
43
25
  > 🌐 Browser extension loaded (plugins: chromium, firefox). Try: web-fetch for static pages or browser-navigate for interactive browsing.
44
26
 
45
- The browser tools are **enabled by default**. You can:
46
-
47
- - **Fetch a static page**: The agent uses `web-fetch` for quick, stateless
48
- content retrieval (no JavaScript, no session).
49
- - **Browse interactively**: The agent uses `browser-navigate` to visit a page,
50
- then clicks, types, scrolls, and screenshots using `@e1`/`@e2` element
51
- references from the accessibility tree.
52
-
53
- > **Tip:** If you only need Markdown content from a static page, `web-fetch`
54
- > is faster and lighter than launching a full browser session.
55
- >
56
- > **Playwright browser binaries are not downloaded during `npm install`.**
57
- > Run `npx playwright install chromium firefox` separately. On first
58
- > `browser-navigate` without them, you'll be prompted with the exact command.
59
-
60
- ---
61
-
62
- ## Extending it
63
-
64
- Beyond the toggle, three surfaces are extensible or shared rather than hardcoded:
65
-
66
- - **Navigation guides** — `web-learn` saves site-specific playbooks that
67
- auto-match by domain and resurface in later sessions. See
68
- [Navigation Guides](#navigation-guides-web-guide--web-learn).
69
- - **Custom browser backends** — if a site blocks the shipped Chromium/Firefox,
70
- drop a `bridge.py` subclass into `~/.pi/agent/pi-lean-portal/user-backends/`
71
- and drive a patched engine like [Camoufox](https://github.com/daijro/camoufox)
72
- yourself. The full flow lives in [Backend Architecture](#backend-architecture)
73
- and [`contributed/README.md`](./contributed/README.md).
74
- - **Toolset toggles** — the `/web` toggle is powered by the shared
75
- [`pi-tool-masking`](https://github.com/coreyryanhanson/pi-tool-masking/) peer dep,
76
- which handles active-set masking, sibling-tool union math, and the
77
- `TOOLSET_EVENTS` protocol that keeps the `browser`/`search` status-bar
78
- glyphs in sync across extensions. It's published as a general toggle
79
- mechanism — any extension that hides/shows its own tools can build on the
80
- same scheme so masking and glyph events stay consistent across the whole
81
- agent, rather than each toggle reinventing its own.
27
+ The browser tools are **enabled by default**: `web-fetch` for static pages,
28
+ `browser-navigate` (plus click/type/scroll/screenshots via `@e` refs) for
29
+ interactive browsing.
82
30
 
83
31
  ---
84
32
 
@@ -90,195 +38,135 @@ toggles guide-saving mode, and manages browser profiles.
90
38
  ### Three-State Toggle
91
39
 
92
40
  | Command | Effect |
93
- |---------|--------|
94
- | `/web on` | **Browsing only** — all interactive browser tools + `web-fetch` are available. `web-learn` is hidden. If `pi-lean-search` is also installed, `web-search` is enabled too. |
95
- | `/web learn` | **Browsing + guide-saving** — same as `on`, plus the `web-learn` tool is available so the AI can save/update navigation guides on request. |
96
- | `/web off` | **All web tools hidden** — saves ~1500–2000 tokens per turn by removing tool schemas from the prompt. `web-fetch` and `web-search` are also hidden. |
41
+ | ------- | ------ |
42
+ | `/web on` | **Browsing only**: all interactive browser tools + `web-fetch` are available. `web-learn` is hidden. If `pi-lean-search` is also installed, `web-search` is enabled too. |
43
+ | `/web learn` | **Browsing + guide-saving**: same as `on`, plus the `web-learn` tool is available so the AI can save/update navigation guides on request. |
44
+ | `/web off` | **All web tools hidden**: saves ~1500–2000 tokens per turn by removing tool schemas from the prompt. `web-fetch` and `web-search` are also hidden. |
97
45
 
98
46
  ### Check Current State
99
47
 
100
48
  | Command | Effect |
101
- |---------|--------|
49
+ | ------- | ------ |
102
50
  | `/web` | Show current toggle status and available sub-commands. |
103
- | `/web status` | **Detailed runtime status** — toggle state, plugin health, active sessions, and profiles on disk. |
104
-
105
- ### Why Toggle?
106
-
107
- - **`/web off`**: When you're done browsing, turning tools off reduces context
108
- usage and keeps the agent focused on coding.
109
- - **`/web learn`**: Only enable this when you want the agent to save navigation
110
- guidance for a site. The agent never calls `web-learn` unprompted — it must
111
- be in learn mode.
112
- - **State persists** across `/reload`, `/resume`, `/fork`, and `/tree`
113
- navigation — the toggle remembers what you chose.
114
-
115
- ### Persistence
116
-
117
- The toggle state is stored in the conversation's branch history. A fresh
118
- conversation starts with the default from `settings.json` — the
119
- `pi-tool-masking` library's `toolsetDefaults` block (see
120
- [Configuration (`settings.json`)](#configuration-settingsjson)), falling back
121
- to the toolset's packaged default (`true` for `web`).
122
-
123
- ---
124
-
125
- ## All 12 Tools
126
-
127
- `pi-lean-portal` registers 12 tools. The first 9 require a **browser session**
128
- (created by `browser-navigate`). `web-fetch`, `web-guide`, and `web-learn` are stateless.
129
-
130
- **Auto-captured screenshots:** `browser-navigate` and `browser-snapshot` automatically
131
- capture a screenshot to a temp file (`/tmp/pi-lean-portal/screenshot-<taskId>.jpg`).
132
- Use the `read` tool to visually inspect the page when the accessibility tree isn't enough.
133
- Screenshots capture the 1280×720 viewport (not full-page) — the same viewport the
134
- browser uses for navigation.
135
-
136
- ### 1. `browser-navigate` — Visit a Page
137
-
138
- Navigates to a URL using a browser plugin (default: Chromium). Returns an
139
- accessibility tree annotated with **@e1, @e2, …** element references.
51
+ | `/web status` | **Detailed runtime status** including toggle state, plugin health, browser-binary presence, active sessions, and profiles on disk. |
52
+ | `/web install` | **Install browser binaries** via the bundled playwright CLI (checkbox dialog in the TUI; prints the manual command elsewhere). |
53
+ | `/web install chromium\|firefox` | Directly download one engine, no dialog. |
140
54
 
141
55
  ```text
142
- Title: Example Domain
143
- URL: https://example.com
144
- Backend: chromium
145
- Interactive elements: 3
146
-
147
- link "More information..." [@e1]
148
- → https://www.iana.org/domains/example
149
- …
150
- ```
151
-
152
- **Parameters:**
153
-
154
- - `url` — the target URL
155
- - `strategy` (optional) — `"auto"` (default) or a plugin name like `"chromium"` or `"firefox"`
156
- - `timeout` (optional) — seconds, default 30, max 120
157
- - `profile` (optional) — `"none"`, `"session"`, or a named profile (see
158
- [Profiles](#profiles--persistent-sessions))
159
-
160
- **Output includes:**
161
-
162
- - Page title, URL, backend name, element count
163
- - Profile info when using profiles
164
- - Bot detection warning when applicable
165
- - Screenshot file path for visual inspection
166
- - Guide footer appended when relevant guides apply
167
-
168
- ### 2. `browser-snapshot` — Refresh the Accessibility Tree
169
-
170
- Returns the current page's accessibility tree with up-to-date `@e` refs. Also
171
- captures a screenshot to a temp file (path in output). Use after clicking, typing,
172
- or scrolling to get fresh element references.
173
-
174
- - `full=true` — returns the complete tree (by default it's compacted to
175
- ~2500 characters for LLM efficiency)
176
-
177
- ### 3. `browser-click` — Click an Element
178
-
179
- ```text
180
- browser-click ref="@e5"
56
+ 🌐 Browser tools: ✅ on | 📖 Learn mode: ❌ off
57
+ ────────────────────────────────────────
58
+ Status: idle
59
+ Plugins: chromium, firefox, chromium-py (disabled), firefox-py (disabled)
60
+ Browsers: chromium ✓ firefox ✓
61
+ Use web-fetch for stateless HTTP fetches.
62
+ Active sessions: 1
63
+ PW [chromium] https://example.com — Example Domain [profile: session]
64
+ Profiles: 1 on disk (named)
65
+ shopping (0.3 KB) ← active
66
+ Session profiles: 1 (manage with /web profile)
181
67
  ```
182
68
 
183
- Clicks the element identified by its `@e` reference. Returns a fresh snapshot
184
- of the page after the click.
185
-
186
- ### 4. `browser-type` — Type Into a Field
187
-
188
- ```text
189
- browser-type ref="@e3" text="hello world"
190
- ```
69
+ ### Persistence
191
70
 
192
- Clears the input, then types the given text. Works on textboxes, searchboxes,
193
- and comboboxes.
71
+ Toggle state persists across `/reload`, `/resume`, `/fork`, and `/tree` — it's
72
+ stored in the conversation's branch history. Fresh conversations start from
73
+ the `toolsetDefaults` block in `settings.json`
74
+ ([Configuration](#configuration-settingsjson)).
194
75
 
195
- ### 5. `browser-scroll` — Scroll the Page
76
+ ---
196
77
 
197
- ```text
198
- browser-scroll direction="down"
199
- ```
78
+ ## Profiles — Persistent Sessions
200
79
 
201
- Scrolls approximately one viewport height. Returns a fresh snapshot.
80
+ Profiles let the AI agent maintain persistent browser state (cookies,
81
+ localStorage) across calls, conversations, and even across different subagents.
202
82
 
203
- ### 6. `browser-back` — Go Back
83
+ ### Profile Modes
204
84
 
205
- Navigates back in browser history. Returns the previous page's snapshot.
85
+ | Mode | `browser-navigate profile=` | Behavior |
86
+ | ---- | --------------------------- | -------- |
87
+ | None | `"none"` | Clean slate every time (no cookies or state) |
88
+ | Session | `"session"` (default) | Persists state for the current conversation; survives `/reload` and `/resume` |
89
+ | Named | `"shopping"`, `"work"`, etc. | Shared across conversations and subagents, similar to browser tabs sharing a profile |
206
90
 
207
- ### 7. `browser-press` — Press a Keyboard Key
91
+ ### Managing Profiles with `/web profile`
208
92
 
209
- ```text
210
- browser-press key="Enter"
211
- ```
93
+ | Sub-command | Effect |
94
+ | ----------- | ------ |
95
+ | `/web profile list` | List all profiles on disk with their state size |
96
+ | `/web profile create shopping` | Create a new named profile |
97
+ | `/web profile session` | Set conversation-scoped default to session mode |
98
+ | `/web profile none` | Reset default to ephemeral (no persistence) |
99
+ | `/web profile shopping` | Switch default profile to an existing named profile |
100
+ | `/web profile clear shopping` | Delete the saved state for a profile (keeps the directory) |
101
+ | `/web profile clear-all --confirm` | Clear ALL profile states |
102
+ | `/web profile prune --confirm` | Remove stale session profiles for ended conversations |
212
103
 
213
- Useful keys: `Enter`, `Tab`, `Escape`, `ArrowDown`, `ArrowUp`, `Backspace`.
104
+ > **How it works:** Profile state is stored at
105
+ > `~/.pi/agent/pi-lean-portal/browser-state/<profile-name>/storage-state.json`.
106
+ > Session-scoped profiles are auto-cleaned when the pi conversation ends.
214
107
 
215
- ### 8. `browser-console` — Read Console / Run JS
108
+ ---
216
109
 
217
- Three modes:
110
+ ## Cookie Management
218
111
 
219
- | Usage | Effect |
220
- |-------|--------|
221
- | `browser-console expression="document.title"` | Evaluates JS and returns the result |
222
- | `browser-console` (no params) | Returns captured console messages (log, warn, error, info) |
223
- | `browser-console clear=true` | Clears the captured console log |
112
+ The `/web cookies` command lets you inspect and clear session cookies:
224
113
 
225
- ### 9. `browser-inspect` — Targeted Element Discovery
114
+ | Sub-command | Effect |
115
+ | ----------- | ------ |
116
+ | `/web cookies list` | List all cookies in the current session (name, value, domain, expiry, flags) |
117
+ | `/web cookies clear --confirm` | Clear ALL cookies for the current session |
226
118
 
227
- A lighter alternative to loading a full snapshot. Queries the page for specific
228
- elements or extracts text content.
119
+ > Cookies are saved as part of profile state. When you switch profiles,
120
+ > the cookies from the old profile are preserved and the new profile's
121
+ > cookies are loaded.
229
122
 
230
- | Parameter | Example | Effect |
231
- |-----------|---------|--------|
232
- | `role` | `"link,button"` | Filter by ARIA role(s) |
233
- | `name` | `"Submit"` | Filter by accessible name (case-insensitive) |
234
- | `ref` | `"e5"` | Look up a specific `@e` ref |
235
- | `subtree` | `"dialog"` | Scope to elements inside a container |
236
- | `text` | `true` | Run DOM text extractor with `@e` annotations |
237
- | `maxChars` | `500` | Limit output length; `0` for full content |
238
- | `query` | `"pricing"` | Keyword filter (only active with `text=true`) |
123
+ ---
239
124
 
240
- > **Tip:** Use `browser-inspect role="dialog"` to quickly check for consent
241
- > dialogs without loading the full tree.
125
+ ## All 12 Tools
242
126
 
243
- ---
127
+ `pi-lean-portal` registers 12 tools. Three are **stateless**; the rest require
128
+ a **browser session** (created by `browser-navigate`).
244
129
 
245
- ## Stateless Fetching (`web-fetch`)
130
+ | Tool | What it does | State |
131
+ | ---- | ------------ | ----- |
132
+ | `web-fetch` | Fetch a URL → Markdown, no browser session | stateless |
133
+ | `web-guide` / `web-learn` | Read / save navigation guides (learn mode via `/web learn`) | stateless |
134
+ | `browser-navigate` | Open a page → accessibility tree with `@e` element refs | session |
135
+ | `browser-snapshot` | Refresh the tree (`full=true` returns the uncompacted tree) | session |
136
+ | `browser-click` / `browser-type` / `browser-scroll` / `browser-back` / `browser-press` | Interact via `@e` refs (click, type, scroll, history, keyboard) | session |
137
+ | `browser-console` | Read captured console messages; also evaluates JS in the page | session |
138
+ | `browser-inspect` | Targeted element/text queries without loading a full snapshot | session |
246
139
 
247
- `web-fetch` is a lightweight, stateless tool that fetches a URL and converts
248
- HTML to Markdown. It does **not** create a browser session.
140
+ ### Automatic Artifacts
249
141
 
250
- **When to use:**
142
+ Session tools save their full output to disk so the agent never needs a second call:
251
143
 
252
- - Static pages, API docs, READMEs, articles
253
- - Quick lookups where JavaScript isn't needed
254
- - When the interactive browser is blocked by bot detection — `web-fetch`
255
- sometimes succeeds where the browser triggers a challenge
144
+ - **Screenshots**: `browser-navigate` and `browser-snapshot` auto-capture a viewport-sized (1280×720) JPEG to `/tmp/pi-lean-portal/screenshot-<taskId>.jpg`[^1].
145
+ - **Full Snapshots**: Any snapshot compacted for size is cached in full at `/tmp/pi-lean-portal/snapshot-*.txt` (last 2 per task).
256
146
 
257
- **Output handling:**
147
+ Both file paths are surfaced as hints in the tool output, which the agent can follow up with the `read` tool.
258
148
 
259
- - Content is truncated to ~4000 characters inline
260
- - Larger content is spilled to a temp file in `/tmp/pi-lean-portal/` — the agent can `read` the file with offset/limit for specific sections. This cache ensures that the agent can access the full page content even when it exceeds the LLM's immediate context window.
261
- - Bot detection and JS-only shells are detected heuristically
149
+ [^1]: Not full-page.
262
150
 
263
151
  ---
264
152
 
265
- ## Navigation Guides (`web-guide` & `web-learn`)
153
+ ## Navigation Guides
266
154
 
267
155
  ### Built-in Pattern Guides
268
156
 
269
157
  `pi-lean-portal` ships with four built-in pattern guides that appear in the guide footer when relevant:
270
158
 
271
159
  | Guide | Trigger | What It Covers |
272
- |-------|---------|----------------|
273
- | `bot-detection` | When bot blocking is detected | Cloudflare, challenge pages, what NOT to do |
274
- | `cookie-consent` | When a dialog is detected in the snapshot | Accept/Reject buttons, Escape key, verification |
160
+ | ----- | ------- | -------------- |
161
+ | `bot-detection` | Bot blocking detected (`botDetected`) | Cloudflare, challenge pages, what NOT to do |
162
+ | `cookie-consent` | Dialog detected (`dialogDetected`) | Accept/Reject buttons, Escape key, verification |
275
163
  | `pagination` | On-demand | Next buttons, infinite scroll, pages |
276
164
  | `search` | On-demand | Search boxes, comboboxes, result lists |
277
165
 
278
166
  ### Overriding Built-in Guides
279
167
 
280
168
  A same-named `.md` file in `~/.pi/agent/pi-lean-portal/web-guides/` (e.g.
281
- `bot-detection.md`) **shadows the builtin entirely** — the whole guide is
169
+ `bot-detection.md`) **shadows the builtin entirely**; the whole guide is
282
170
  replaced, not field-merged. To keep a pattern guide firing, include
283
171
  `trigger.signal: botDetected` (or `dialogDetected`) in the frontmatter;
284
172
  omitting it disables the trigger.
@@ -297,270 +185,87 @@ Site guides and pattern guides live in **disjoint namespaces** — a site guide
297
185
  for `www.botdetection.com` does not collide with the `bot-detection` pattern
298
186
  guide; both fire when applicable.
299
187
 
300
- ### Viewing Guides
301
-
302
- ```text
303
- web-guide → lists all available guides
304
- web-guide guide="cookie-consent" → shows guidance text
305
- ```
188
+ ### Domain Matching
306
189
 
307
- The output includes the guide's last updated date and source (`builtin` or
308
- `user`).
309
-
310
- ### Creating Your Own Site Guides (`web-learn`)
311
-
312
- When the agent is in **learn mode** (`/web learn`), it can save or update
313
- navigation guidance for specific sites using `web-learn`.
314
-
315
- ```text
316
- web-learn domain="reddit.com" content="…guidance text…"
317
- ```
190
+ A guide's declared domain matches the **exact hostname or any subdomain of
191
+ it**. Declaring `reddit.com` covers `www.reddit.com`, `old.reddit.com`, and
192
+ any other subdomain, so a guide saved once resurfaces across a site's URL
193
+ variations. This is what makes guides cheap to author: you rarely need more
194
+ than the apex domain. A domain may match multiple guides (a web guide plus
195
+ one or more API guides) — all matching guides surface together, sorted
196
+ host-first (API guides before web guides).
318
197
 
319
- This creates a `.md` file with YAML frontmatter in `~/.pi/agent/pi-lean-portal/web-guides/`.
320
- The guide becomes available immediately via `web-guide` and appears in the guide footer on
321
- future navigations to that domain.
198
+ ### API guides from `pi-lean-host` (co-install)
322
199
 
323
- **Learn mode is off by default** — the agent never saves guides unprompted.
324
- You must explicitly enable it with `/web learn`.
325
-
326
- ---
327
-
328
- ## `/web status` — Detailed Runtime Status
329
-
330
- ```text
331
- /web status
332
- ```
333
-
334
- Shows everything about the browser runtime in one notification:
335
-
336
- ```text
337
- 🌐 Browser tools: ✅ on | 📖 Learn mode: ❌ off
338
- ────────────────────────────────────────
339
- Status: idle
340
- Plugins: chromium, firefox, chromium-py (disabled), firefox-py (disabled)
341
- Use web-fetch for stateless HTTP fetches.
342
- Active sessions: 1
343
- PW [chromium] https://example.com — Example Domain [profile: session]
344
- Profiles: 1 on disk (named)
345
- shopping (0.3 KB) ← active
346
- Session profiles: 1 (manage with /web profile)
347
- ```
348
-
349
- Covers:
350
-
351
- - **Toggle state** — whether browser/learn tools are active in the agent's context
352
- - **Backend health** — idle, busy, or error state
353
- - **All registered plugins** — enabled/disabled status
354
- - **Active sessions** — current URL, title, profile name per session
355
- - **Profiles on disk** — named profiles with state size and which is
356
- currently active; session profiles are collapsed into a single count line
357
- (inspect individually with `/web profile list`)
358
-
359
- When `pi-lean-search` is also installed, the status bar shows two independent
360
- glyphs: `● idle` (browser state) and `● searxng` (search health/state).
361
-
362
- ---
363
-
364
- ## Profiles — Persistent Sessions
365
-
366
- Profiles let the AI agent maintain persistent browser state (cookies,
367
- localStorage) across calls, conversations, and even across different subagents.
368
-
369
- ### Profile Modes
370
-
371
- | Mode | `browser-navigate profile=` | Behavior |
372
- |------|-----------------------------|----------|
373
- | None | `"none"` | Clean slate every time — no cookies, no state |
374
- | Session | `"session"` (default) | Persists state for the current conversation; survives `/reload` and `/resume` |
375
- | Named | `"shopping"`, `"work"`, etc. | Shared across conversations and subagents — like browser tabs sharing a profile |
376
-
377
- ### Managing Profiles with `/web profile`
378
-
379
- All profile management happens through the `/web profile` command:
380
-
381
- | Sub-command | Effect |
382
- |-------------|--------|
383
- | `/web profile list` | List all profiles on disk with their state size |
384
- | `/web profile create shopping` | Create a new named profile |
385
- | `/web profile session` | Set conversation-scoped default to session mode |
386
- | `/web profile none` | Reset default to ephemeral (no persistence) |
387
- | `/web profile shopping` | Switch default profile to an existing named profile |
388
- | `/web profile clear shopping` | Delete the saved state for a profile (keeps the directory) |
389
- | `/web profile clear-all --confirm` | Clear ALL profile states |
390
- | `/web profile prune --confirm` | Remove stale session profiles for ended conversations |
391
-
392
- > **How it works:** Profile state is stored at
393
- > `~/.pi/agent/pi-lean-portal/browser-state/<profile-name>/storage-state.json`.
394
- > Session-scoped profiles use names like `_session-<piSessionId>` and are
395
- > auto-cleaned when the pi conversation ends.
396
-
397
- ---
398
-
399
- ## Cookie Management
400
-
401
- The `/web cookies` command lets you inspect and clear session cookies:
402
-
403
- | Sub-command | Effect |
404
- |-------------|--------|
405
- | `/web cookies list` | List all cookies in the current session (name, value, domain, expiry, flags) |
406
- | `/web cookies clear --confirm` | Clear ALL cookies for the current session |
407
-
408
- > Cookies are saved as part of profile state. When you switch profiles,
409
- > the cookies from the old profile are preserved and the new profile's
410
- > cookies are loaded.
200
+ When `pi-lean-host` is installed alongside portal and `/api` is on, its
201
+ user-authored API guides also **surface in the navigate footer** alongside
202
+ your web guides using the same reactive mechanism, with no extra setup. API guides sort
203
+ first (API access is cheaper than browsing) but both always appear, so a
204
+ partial-coverage API keeps its web guide for the gaps the API doesn't cover.
205
+ The footer routes API guides to `api-guide({domain, guide})` rather than
206
+ `web-guide`. See the
207
+ [host README](https://github.com/coreyryanhanson/pi-lean-dimension/blob/main/packages/pi-lean-host/README.md#co-installing-with-pi-lean-portal)
208
+ for the host side of the contract.
411
209
 
412
210
  ---
413
211
 
414
212
  ## Backend Architecture
415
213
 
416
214
  `pi-lean-portal` uses a **plugin-based architecture**. The core framework is
417
- backend-agnostic — plugins implement a standard `BrowserPlugin` interface,
215
+ backend-agnostic; plugins implement a standard `BrowserPlugin` interface,
418
216
  and the router dispatches tool calls to the right plugin based on a
419
217
  `strategy` parameter.
420
218
 
421
219
  ### Four Shipped Backends
422
220
 
423
- The extension ships with **four browser backends** out of the box:
424
-
425
221
  | Backend | Engine | Type | Default |
426
- |---------|--------|------|---------|
222
+ | ------- | ------ | ---- | ------- |
427
223
  | `chromium` | Chromium | Node/Playwright | **Enabled** (auto strategy) |
428
224
  | `firefox` | Firefox | Node/Playwright | **Enabled** |
429
225
  | `chromium-py` | Chromium | Python/Playwright | Disabled |
430
226
  | `firefox-py` | Firefox | Python/Playwright | Disabled |
431
227
 
432
- > **Install Firefox:** `npx playwright install firefox` to use the Node
433
- > `firefox` backend. For the Python parity backends, install Playwright
434
- > inside `backends/python-base/.venv`.
435
-
436
- ### Plugin Capabilities
228
+ > **Install Firefox:** run `/web install firefox` inside pi to download the
229
+ > Firefox binary for the Node `firefox` backend. For the Python parity
230
+ > backends, install Playwright inside `backends/python-base/.venv`.
437
231
 
438
- All four shipped backends share the same capability set (they all use
439
- Playwright under the hood). The router adapts based on each plugin's
440
- capability advertisement:
232
+ All four shipped backends support screenshots (viewport-sized, not
233
+ full-page), console capture, JS evaluation, bot detection, and dialog
234
+ auto-dismissal. The one divergence:
235
+ `AbortSignal` is advertised but silently ignored on the Python `-py` backends.
441
236
 
442
- | Capability | Chromium / Firefox (Node) | `-py` backends (Python) |
443
- |------------|--------------------------|------------------------|
444
- | Full-page screenshots | ✅ | ✅ |
445
- | Console message capture | ✅ | ✅ |
446
- | JavaScript evaluation | ✅ | ✅ |
447
- | Bot detection | ✅ | ✅ |
448
- | Dialog auto-dismissal | ✅ | ✅ |
449
- | AbortSignal support | ✅ | Advertised, silently ignored |
450
-
451
- > The `-py` Python backends are disabled by default. They ship as parity
452
- > references for the Python bridge contract and as templates for users
453
- > authoring their own Python-based backends (see the custom backends
454
- > section below). Keep `chromium-py` as a reference when reading the
455
- > bridge code.
237
+ > The `-py` backends are disabled by default, as they ship as parity references
238
+ > for the Python bridge contract and as templates for authoring your own
239
+ > Python-based backends.
456
240
 
457
241
  ### How Plugin Selection Works
458
242
 
459
- - The **order** of plugins in the config array determines priority — the
243
+ - The **order** of plugins in the config array determines priority; the
460
244
  first enabled plugin is the "auto" default (typically Chromium).
461
245
  - The **AI agent explicitly selects** which backend to use via the
462
246
  `strategy` parameter in `browser-navigate`:
463
247
  - `strategy="auto"` → uses the first enabled plugin (typically Chromium)
464
248
  - `strategy="firefox"` → uses the Firefox Node backend
465
249
  - `strategy="chromium-py"` → uses the Python Chromium backend
466
- - **No automatic fallbacks** and **no mid-session transitions** — if a
250
+ - **No automatic fallbacks** and **no mid-session transitions**. If a
467
251
  plugin fails, the agent decides what to do next.
468
- - The extension auto-detects whether a plugin is Node-based (`index.ts`)
469
- or Python-based (`bridge.py`) by inspecting the directory.
470
252
 
471
253
  ### Stealth & Custom Browser Backends
472
254
 
473
- This package ships four backends — `chromium`, `firefox`, `chromium-py`,
474
- and `firefox-py` — all built on Playwright. Additional browser support
475
- (including stealth engines like **Camoufox**) is intentionally **left
476
- to users** to author and drop in, rather than being bundled with the
477
- package. The infrastructure for this is now in place: a **quirks system**
478
- lets a backend declare how it diverges from the base Playwright behavior,
479
- and a **config channel** (`browser.init` RPC) forwards launch options
480
- from `settings.json` to the Python bridge subprocess.
481
-
482
- Most users will never need a stealth backend (see
483
- [`contributed/CHOOSING.md`](./contributed/CHOOSING.md) for when to reach
484
- for one at all). When you do, the flow is:
485
-
486
- 1. **Drop a `bridge.py` into the user-backends tree.** The convention is
487
- `~/.pi/agent/pi-lean-portal/user-backends/<name>-py/bridge.py` (the
488
- `-py` suffix mirrors the shipped `chromium-py` / `firefox-py`). This is
489
- a separate tree from the package's own `backends/` directory, which is
490
- not edited after install — exactly so custom backends survive updates.
491
- 2. **Create a venv and fetch the engine binary** (e.g.
492
- `python -m camoufox fetch`). The shared `pi_browser_bridge` library is
493
- injected onto `PYTHONPATH` automatically at spawn time, so you do not
494
- need to `pip install` it.
495
- 3. **Register it in `browser.plugins`** with an **absolute** `pythonPath`
496
- and a `launch` object whose keys are forwarded to the bridge.
497
- 4. **Verify with `/web status`** and a `browser-navigate`.
498
-
499
- The shipped **Camoufox template** at
500
- [`contributed/camoufox-py/bridge.py`](./contributed/camoufox-py/bridge.py)
501
- is a worked example — copy it as a starting point. The full install flow,
502
- the quirks schema reference, and the security model (user-backends are
503
- **trusted user code** — never auto-downloaded, no plugin marketplace) live
504
- in [`contributed/README.md`](./contributed/README.md). The decision doc at
505
- [`contributed/CHOOSING.md`](./contributed/CHOOSING.md) covers when to use
506
- a stealth backend at all and the two lifecycle patterns for implementing
507
- your own.
508
-
509
- #### Writing your own backend (high level)
510
-
511
- A custom Python backend is a subclass of `PlaywrightBridge`
512
- (`backends/python-base/pi_browser_bridge/playwright_base.py`) that sets
513
- the **quirks flags** its engine needs as class attributes and overrides
514
- the launch hook matching how the engine owns Playwright. The flags
515
- (`_fingerprint_managed_context`, `_eval_prefix`, `_scroll_via_wheel`,
516
- `_skip_default_viewport`, `_skip_networkidle`, `_wrap_mw_eval_in_eval`)
517
- all default off, so a subclass that sets none of them is bit-identical to
518
- the shipped `chromium-py` / `firefox-py`. The full table with effects is
519
- in [`contributed/README.md`](./contributed/README.md#quirks-schema-reference).
520
-
521
- Node-based custom backends follow the same shape via the
522
- `PlaywrightPluginBase` class — the auto-detection in `plugin-loading`
523
- picks up `index.ts` (Node) or `bridge.py` (Python) entry points from the
524
- user-backends directory.
525
-
526
- #### Tests are auto-discovered
527
-
528
- You usually do **not** need to write your own tests. The contributed
529
- runner at `__tests__/run-contributed-suites.test.ts` discovers every
530
- backend under `user-backends/*-py/`, loads config from the test-local
531
- `settings.json`, and runs the shared contract + persistence + parity +
532
- quirks-introspection suites against it — forwarding your configured
533
- `launch` options. Opt in with `CONTRIB_RUN=1`:
534
-
535
- ```bash
536
- npm run setup:miniwob # one-time: clone MiniWoB++ content
537
- CONTRIB_RUN=1 npx vitest run packages/pi-lean-portal/__tests__/run-contributed-suites.test.ts
538
- ```
539
-
540
- A custom backend's config entry looks like:
541
-
542
- ```jsonc
543
- {
544
- "browser": {
545
- "plugins": [
546
- { "name": "chromium", "dir": "chromium", "enabled": true, "config": {} },
547
- { "name": "firefox", "dir": "firefox", "enabled": true, "config": {} },
548
- { "name": "camoufox-py", "dir": "camoufox-py", "enabled": true, "config": {
549
- "pythonPath": "/home/me/.pi/agent/pi-lean-portal/user-backends/camoufox-py/.venv/bin/python",
550
- "launch": { "headless": true, "os": "windows", "humanize": true }
551
- }
552
- }
553
- ]
554
- }
555
- }
556
- ```
557
-
558
- `pythonPath` must be **absolute**; `dir` resolves against the user-backends
559
- root (multi-root discovery: package `backends/` → `USER_BACKENDS_DIR` →
560
- absolute). `launch` keys are forwarded to the bridge as
561
- `plugin_config.launch` via the `browser.init` RPC. Stealth backends are
562
- never in the default fallback list — a fresh install with no
563
- `browser.plugins` loads only the four shipped backends.
255
+ Stealth engines like **Camoufox** are intentionally **left to users** to
256
+ author: drop a `bridge.py` into
257
+ `~/.pi/agent/pi-lean-portal/user-backends/<name>-py/` — a separate tree
258
+ from the package's `backends/` directory (never edited after install,
259
+ so custom backends survive updates) and never in the default fallback list.
260
+ Register one in `browser.plugins` with an **absolute** `pythonPath` (see
261
+ [Configuration](#configuration-settingsjson)).
262
+
263
+ The full install flow, the quirks schema, and the worked Camoufox template
264
+ live in
265
+ [`contributed/README.md`](https://github.com/coreyryanhanson/pi-lean-dimension/blob/main/packages/pi-lean-portal/contributed/README.md);
266
+ [`contributed/CHOOSING.md`](https://github.com/coreyryanhanson/pi-lean-dimension/blob/main/packages/pi-lean-portal/contributed/CHOOSING.md)
267
+ covers when to reach for one at all (most users never need one).
268
+ Node-based custom backends follow the same shape via `PlaywrightPluginBase`.
564
269
 
565
270
  ---
566
271
 
@@ -580,7 +285,7 @@ Controls which browser backends are loaded. Entries are processed in order
580
285
  "plugins": [
581
286
  {
582
287
  "name": "chromium", // Required: unique plugin identifier
583
- "dir": "chromium", // Required: directory under backends/
288
+ "dir": "chromium", // Required: backend directory
584
289
  "enabled": true, // Optional, defaults to true
585
290
  "config": {} // Optional, passed to the plugin's init()
586
291
  }
@@ -589,11 +294,9 @@ Controls which browser backends are loaded. Entries are processed in order
589
294
  }
590
295
  ```
591
296
 
592
- Each entry requires only a unique name, a backend directory path, and an
593
- optional `config` object passed to the plugin's `init()`. For the Python
594
- backends, `config` carries `pythonPath` and a `launch` object (the shape
595
- shown for `camoufox-py` in the [Stealth & Custom Browser Backends](#stealth--custom-browser-backends)
596
- section above is the reference for a user-authored Python backend).
297
+ A user-installed stealth backend additionally puts an **absolute** `pythonPath`
298
+ and a `launch` object into `config` — see
299
+ [`contributed/README.md`](https://github.com/coreyryanhanson/pi-lean-dimension/blob/main/packages/pi-lean-portal/contributed/README.md#6-register-in-settingsjson).
597
300
 
598
301
  ### `browser.defaultProfile`
599
302
 
@@ -608,7 +311,7 @@ a `profile` parameter:
608
311
  }
609
312
  ```
610
313
 
611
- ### `toolsetDefaults` *(settings-based toolset defaults)*
314
+ ### `toolsetDefaults`
612
315
 
613
316
  Whether browser tools are enabled on fresh conversations. Read by the
614
317
  `pi-tool-masking` library at restore time, between the chat-branch tier and
@@ -628,10 +331,8 @@ the toolset's packaged default:
628
331
  - Omit a `toolsetDefaults` key to use the toolset's packaged default (`web`
629
332
  and `search` default `true`; `web-learn` defaults `false`).
630
333
  - The `search` key only applies when `pi-lean-search` is installed.
631
-
632
- > The legacy `browserToggle.defaultEnabled` key is **removed in 0.4.0** — it
633
- > is no longer read. If you pinned it, move the value into the matching
634
- > `toolsetDefaults` entry above.
334
+ - Pins do not apply in spawned subagent children (see pi-tool-masking
335
+ 1.3.0's `piToolMasking.childPolicy` for the opt-out).
635
336
 
636
337
  ### `browser.maxStorageStateSize`
637
338
 
@@ -652,61 +353,30 @@ Size threshold for profile state warnings (default: 10 MB):
652
353
  ### When to use `web-fetch` vs `browser-navigate`
653
354
 
654
355
  | Use `web-fetch` | Use `browser-navigate` |
655
- |-----------------|----------------------|
356
+ | --------------- | ---------------------- |
656
357
  | Static content, docs, READMEs | Interactive pages, JS-heavy SPAs |
657
358
  | Quick lookups, no session needed | Form filling, clicking, authentication |
658
- | Bot-detected pages (fallback) | Visual inspection (auto-captured screenshots) |
659
- | Content you want as clean Markdown | Pages where you need the accessibility tree |
660
-
661
- ### Working with `@e` Element References
662
-
663
- - `@e1`, `@e2`, etc. are assigned based on the accessibility tree order
664
- - `browser-click`/`type`/`scroll` already return a fresh snapshot and cache
665
- the full tree to disk — no separate `browser-snapshot` needed unless you
666
- want the uncompacted tree (`full=true`) or a screenshot
667
- - `browser-inspect` is cheaper than `browser-snapshot full=true` for finding
668
- specific elements
359
+ | Content you want as clean Markdown | Visual inspection (auto-captured screenshots), pages where you need the accessibility tree |
669
360
 
670
- ### Navigating Large Pages
361
+ ### Working with `@e` Element References & Large Pages
671
362
 
672
- - Snapshots are automatically compacted to ~2500 characters
673
- - Very large pages (>8000 chars) preserve the top ~2000 chars
674
- - The **full tree is cached to disk** at `/tmp/pi-lean-portal/snapshot-*.txt`
675
- when it would otherwise be truncated — use `read` on the cache file with
676
- offset/limit to retrieve the complete tree
677
- - `browser-inspect text=true query="keyword"` finds specific content without
678
- loading the full tree
363
+ The agent interacts via `@e1`, `@e2` refs from the accessibility tree —
364
+ no CSS selectors or XPath. Snapshots are auto-compacted to ~2500 chars;
365
+ full trees and screenshots spill to `/tmp/pi-lean-portal/` (paths are
366
+ surfaced in tool output).
679
367
 
680
368
  ### Bot Detection
681
369
 
682
- When a page triggers anti-automation:
683
-
684
- 1. The agent sees a warning in the navigate output
685
- 2. The **bot-detection guide** footer appears with strategies available via `web-guide`
686
- 3. If very few elements are detected (<5), the navigation is treated as
687
- a hard failure — the agent won't try to interact with a challenge page
688
- 4. Retry with a stealth backend — the `browser-navigate` `strategy`
689
- parameter lists registered backend names; a stealth backend (e.g.
690
- `strategy="camoufox"`) can pass challenges the default `chromium`/
691
- `firefox` triggers. Only names listed in the `strategy` description
692
- are valid — there is no `"stealth"` alias
693
- 5. Try `web-fetch` on the same URL — it skips JS execution, so it can
694
- retrieve raw HTML on pages that block the interactive browser via
695
- client-side fingerprinting (it won't help against server-side WAFs)
696
-
697
- ### Guide Creation Discipline
698
-
699
- When creating navigation guides with `web-learn`:
700
-
701
- - Keep guidance **concise** (≤800 chars recommended)
702
- - Focus on: page structure, consent dialogs, known quirks, and useful
703
- selector patterns
704
- - Include domains so the guide appears in the footer on future navigations
705
- - No runtime enforcement on guide length, but brevity helps the LLM
370
+ When a page triggers anti-automation, the agent sees a warning plus a
371
+ bot-detection guide footer, and challenge pages with <5 visible elements
372
+ fail hard rather than being interacted with. If a site consistently blocks
373
+ the shipped Chromium/Firefox, that's the trigger to install a stealth
374
+ backend (see [Stealth & Custom Browser Backends](#stealth--custom-browser-backends)) —
375
+ not something to configure up front.
706
376
 
707
377
  ### Security
708
378
 
709
- - URLs are parsed with `new URL()` as input validation — malformed
379
+ - URLs are parsed with `new URL()` as input validation. Malformed
710
380
  URLs are rejected, but no SSRF boundary is enforced (a coding agent
711
381
  already has filesystem and shell access, so blocking localhost or
712
382
  private IPs would be theater)
@@ -715,9 +385,4 @@ When creating navigation guides with `web-learn`:
715
385
 
716
386
  ---
717
387
 
718
- > `pi-lean-portal` is part of the
719
- > [pi-lean-dimension](https://github.com/coreyryanhanson/pi-lean-dimension)
720
- > web-tools suite. For questions, issues, or feature requests, check the
721
- > project's documentation or open an issue.
722
- >
723
388
  > License: AGPL-3.0-only