fix(web-access): use governed external browser open tool (#97)

## 中文

### 变更
- 将 web-access 升级到 v3.4.3,并声明 `BrowserExternalOpen`
- 外部浏览器的简单打开/导航固定为 `BrowserExternalProbe` → `BrowserExternalOpen`
- 仅把点击、动态取文、提取等高级交互交给隔离 Playwright
- 同步中英文 Skill、状态表、决策树和 CDP reference

### 验证
- `python scripts/i18n/validate-i18n.py`
- `python scripts/i18n/translate.py --check skills/web-access`
- `python -m unittest scripts/i18n/test_validate_i18n.py`
- `git diff --check`

## English

### Changes
- Bump web-access to v3.4.3 and expose `BrowserExternalOpen`
- Route simple external-browser open/navigation through
`BrowserExternalProbe` → `BrowserExternalOpen`
- Reserve isolated Playwright for advanced click, dynamic-read, and
extraction workflows
- Keep the English/Chinese skill, status table, decision tree, and CDP
reference aligned

### Verification
- `python scripts/i18n/validate-i18n.py`
- `python scripts/i18n/translate.py --check skills/web-access`
- `python -m unittest scripts/i18n/test_validate_i18n.py`
- `git diff --check`
This commit is contained in:
2026-08-29 07:53:59 -04:00
committed by GitHub
parent 7026cc72ed
commit 3059468526
4 changed files with 59 additions and 21 deletions

View File

@@ -15,7 +15,7 @@ description: >-
新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、
推特、X、知乎、公众号、已登录、登录状态。
license: Complete terms in LICENSE.txt
version: 3.4.2
version: 3.4.3
type: procedural
risk_level: low
status: enabled
@@ -32,6 +32,7 @@ provides:
tools:
- BrowserManage
- BrowserExternalProbe
- BrowserExternalOpen
- BrowserSnapshot
- BrowserAct
- BrowserScript
@@ -54,14 +55,14 @@ metadata:
short_desc: 联网搜索、网页抓取、内置受管浏览器登录态访问与取文、研究调研工作流
description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome/Edge/Chromium。
body: ./SKILL.zh-CN.md
source_hash: sha256:b12669d544fe2c7e
source_hash: sha256:78082a20359f730f
translated_by: human
en-US:
name: Web Access
short_desc: Web search, page fetching, logged-in access via the governed built-in browser, research workflows
description: A web-access toolkit — search public pages, fetch heavy pages via Jina Reader, reach and read logged-in sites through the governed built-in browser, and drive the user's named Chrome/Edge/Chromium over CDP on request.
body: ./SKILL.md
source_hash: sha256:b12669d544fe2c7e
source_hash: sha256:78082a20359f730f
translated_by: human
market:
icon: >-
@@ -102,18 +103,19 @@ web-access is a **procedural skill** that provides four complementary layers of
- **L2** (Jina Reader): JS-rendered heavy pages, saving tokens by default
- **L3** (governed built-in browser, capability surface completed in v3.0): reach, *interact with*, and **read** logged-in / interactive sites — isolated BrowserSpace per task, zero Python dependency, every action carries a signed receipt. Bulk text extraction (`page.extract-text`), discriminated waits (`page.wait`), and code mode (`BrowserScript`) all close the loop inside this layer
- **L3-external** (the user's named Chrome/Edge/Chromium, attached via CDP + Python Playwright): **take this route only when the user names their own browser or explicitly accepts it after you explain why**its isolated DesireCore profile login state, its visible window, and the user's ability to take over at any moment
- **L3-external** (the user's named Chrome/Edge/Chromium, opened through governed CDP tools and attached via isolated Python Playwright only for advanced interaction): **take this route only when the user names their own browser or explicitly accepts it after you explain why**the login state the user established manually in the DesireCore-isolated external profile, the visible window, and the user's ability to take over at any moment
A note of history on L3-external: v3.0 deleted it outright, on the grounds that "every technical reason it existed for (no bulk text channel, evaluate unusable, screenshots must activate-serialize) is now covered by the built-in browser". That technical judgement was correct — **as a fallback for when the built-in browser isn't enough, it genuinely isn't needed any more**. But the deletion took with it a completely different use case: the user wanting *their own* browser. That has nothing to do with capability, and the built-in browser cannot stand in for it, so v3.2 restores it as a peer option **triggered by user intent**. Note it is no longer a fallback; see "Two browsers — pick by user intent" below.
### v3.0: governed built-in browser (default-hidden, exposed only after Skill activation)
When you call `Skill('web-access')`, the following tools are injected into the current session. The `Browser*` tools drive the built-in browser; `BrowserExternalProbe` only inspects external-browser prerequisites:
When you call `Skill('web-access')`, the following tools are injected into the current session. Most `Browser*` tools drive the built-in browser; `BrowserExternalProbe` and `BrowserExternalOpen` are the explicitly named external-browser exceptions:
| Tool | Purpose |
|------|---------|
| BrowserManage | Create/destroy isolated BrowserSpace, start sessions, manage tabs |
| BrowserExternalProbe | Read-only check for installed Chrome/Edge/Chromium and loopback CDP readiness; never launches or reads a profile |
| BrowserExternalOpen | After `BrowserExternalProbe` returns `ready` in the same runtime session, visibly open one HTTP(S) URL in the exact external browser; no shell, Python, or Playwright |
| BrowserSnapshot | `semantic` / `text` / `accessibility` / `visual` snapshots — the primary way to read a page |
| BrowserAct | One governed action per call: navigate, input, extract text, wait, element ops, screenshot, … |
| BrowserScript | **Code mode**: one async JS script issues browser commands back-to-back, eliminating per-action round trips (trust level equals Bash) |
@@ -168,7 +170,7 @@ Handle the structured result exactly:
| status | Required response |
|---|---|
| `ready` | Continue with Playwright `connect_over_cdp`; name the detected external browser honestly. For `any`, the already-ready endpoint is the prepared choice even if other products are installed |
| `ready` | For a simple open/navigation, call `BrowserExternalOpen` with the returned exact browser id and port. For advanced click/read/extract interaction, continue with isolated Playwright `connect_over_cdp`. Name the detected external browser honestly. For `any`, the already-ready endpoint is the prepared choice even if other products are installed |
| `browser_not_installed` | Say the requested browser was not detected. If `alternatives` is non-empty, ask whether the user wants one of them; **never switch automatically** |
| `debug_port_closed` | Show the returned `launchCommand`, ask the user to launch it and log in manually, then wait and probe again |
| `browser_choice_required` | No endpoint is ready and multiple external browsers are installed. List id/name only and ask which one the user wants; probe that exact choice next |
@@ -180,7 +182,7 @@ Handle the structured result exactly:
1. The user logs in manually to the sites they need.
2. That external browser window stays open.
3. Call `BrowserExternalProbe` again. Only `ready` authorizes the CDP attach attempt.
3. Call `BrowserExternalProbe` again. Only `ready` authorizes `BrowserExternalOpen` or an advanced CDP attach attempt.
Do not replace this probe with `curl`: a refused connection cannot distinguish “browser not installed”
from “browser installed but debugging disabled”, and a random HTTP service must not be accepted as CDP.
@@ -188,9 +190,21 @@ from “browser installed but debugging disabled”, and a random HTTP service m
⚠️ When attached over CDP, **never call `browser.close()`** — that would close the user's own external browser.
Only close the page you opened. Full recipes in [references/cdp-browser.md](references/cdp-browser.md).
### Simple external open/navigation: use the dedicated tool
When the user's requested action is only to open or navigate to an HTTP(S) URL in their named external browser:
1. Call `BrowserExternalProbe` and require `ready`.
2. In the same runtime session, call `BrowserExternalOpen` with `browser: detectedBrowser.id`, the exact probe `port`, and the URL.
3. Do not call Bash, PowerShell, Python, pip, or Playwright for this simple action.
The ready grant is short-lived, one-time, and bound to the exact browser and port. The host revalidates
`Browser.getVersion` on the same CDP WebSocket before creating the target. If open reports a closed,
invalid, or mismatched endpoint, call the probe again and follow its status guidance; do not retry with shell.
### Platform-safe Playwright execution
After a `ready` probe, identify the current OS **before** creating or running an attach script:
Only for advanced interaction after a `ready` probe, identify the current OS **before** creating or running an attach script:
1. **Create or select the DesireCore-owned isolated venv first.** Do not probe a global interpreter
for Playwright. A system/bootstrap Python may only run `-m venv` when the venv does not exist.
@@ -217,7 +231,9 @@ User intent
├─ **Any request that names "my own / my machine's / external Chrome/Edge/Chromium"**
│ └─→ L3-external first, whether the verb is search/read/open/click:
│ BrowserExternalProbe(exact requested browser), then connect_over_cdp() only on `ready`
│ BrowserExternalProbe(exact requested browser), then only on `ready`:
│ simple open/navigation → BrowserExternalOpen(exact detected browser + port + URL)
│ advanced search/read/click/extract → isolated Playwright connect_over_cdp()
│ Otherwise follow the status guidance and wait; never route to WebSearch/WebFetch/Jina/built-in browser
├─ "Local browser" without saying built-in or external
@@ -262,7 +278,7 @@ DesireCore can drive **two** browsers. They are peer options:
| | L3 built-in governed browser | L3-external — the user's own browser |
|---|---|---|
| What it is | A browser instance inside the app (the `Browser*` tools) | The user-named Chrome/Edge/Chromium on their machine, attached via CDP + Python Playwright |
| What it is | A browser instance inside the app (the governed built-in browser tools) | The user-named Chrome/Edge/Chromium on their machine, opened through governed CDP tools and attached via isolated Python Playwright only for advanced interaction |
| Login state | Isolated; needs `browser.import.*` granted by the Host before `BrowserImport` can pull cookies | **Literally the user's own session** — nothing to import |
| Can the user see it | Agent tabs are offscreen by default; must be presented to the workbench | **It's their own window** — visible throughout, theirs to take over |
| Prerequisite | None | `BrowserExternalProbe` must report `ready`; otherwise follow its exact status guidance |
@@ -301,7 +317,7 @@ When what they asked for and what you're giving differ, the wording has to make
| L1 | Public, static | `WebFetch` | Low |
| L2 | JS-heavy, long articles, token savings | `Bash curl r.jina.ai` | **Lowest** (Markdown pre-cleaned) |
| **L3** | **Login-gated navigation, interaction & extraction (PRIMARY)** | **built-in browser (BrowserManage / BrowserAct / BrowserSnapshot / BrowserScript)** | Medium |
| L3-external | **User named their own browser**, or explicitly accepted this route after you explained why it is needed | `BrowserExternalProbe` → platform-native shell (`PowerShell` on Windows) + DesireCore isolated venv + Playwright `connect_over_cdp` (see references/cdp-browser.md) | Medium |
| L3-external | **User named their own browser**, or explicitly accepted this route after you explained why it is needed | Simple open/navigation: `BrowserExternalProbe``BrowserExternalOpen`; advanced interaction: ready probe → platform-native shell (`PowerShell` on Windows) + DesireCore isolated venv + Playwright `connect_over_cdp` (see references/cdp-browser.md) | Medium |
**Default priority**: L1 for simple public pages → L2 for heavy → **L3 for login-gated (body text and in-site API data included)**.

View File

@@ -16,18 +16,19 @@ web-access 是一个**流程型技能Procedural Skill**,提供四层互
- **L2**Jina ReaderJS 渲染的重页面,默认节省 Token
- **L3**内置受管浏览器v3.0 能力面补全):到达、操作并**读取**登录态/交互站点——每个任务独立 BrowserSpace 隔离、零 Python 依赖、每次动作都有可审计回执。批量取文(`page.extract-text`)、判别式等待(`page.wait`)、代码模式(`BrowserScript`)都在本层内闭环
- **L3-external**(用户点名的 Chrome/Edge/Chromium,经 CDP + Python Playwright 接管):**仅在用户点名,或 Agent 解释原因后用户明确同意时走这条**——DesireCore 隔离外部 Profile 中由用户手工登录形成的状态、可见窗口,以及用户随时接管的能力
- **L3-external**(用户点名的 Chrome/Edge/Chromium;简单打开由受治理 CDP 工具完成,只有高级交互才经隔离 Python Playwright 接管):**仅在用户点名,或 Agent 解释原因后用户明确同意时走这条**——DesireCore 隔离外部 Profile 中由用户手工登录形成的状态、可见窗口,以及用户随时接管的能力
关于 L3-external 的一段历史v3.0 曾把它整个删掉理由是「它存在的每一条技术理由无批量取文通道、evaluate 不可用、截图必须串行 activate都已被内置浏览器覆盖」。那个技术判断没错**作为「内置浏览器不够用时的兜底」它确实不再需要**。但删除时顺带丢掉了一个完全不同的用例:用户想用**他自己那个**浏览器。这跟能力够不够无关,内置浏览器替代不了,所以 v3.2 把它作为一条**由用户意图触发**的平级选择恢复回来——注意它不再是 fallback判据见下方「两个浏览器按用户意图选」。
### v3.0:内置受管浏览器(默认隐藏,激活后才暴露)
调用 `Skill('web-access')` 加载本技能时会注入以下工具。`Browser*` 工具驱动内置浏览器;`BrowserExternalProbe` 只检查外部浏览器前置条件
调用 `Skill('web-access')` 加载本技能时会注入以下工具。大多数 `Browser*` 工具驱动内置浏览器;`BrowserExternalProbe` `BrowserExternalOpen` 是名称明确的外部浏览器例外
| 工具 | 用途 |
|------|------|
| BrowserManage | 建/销隔离 BrowserSpace、启动会话、管理标签页 |
| BrowserExternalProbe | 只读检查 Chrome/Edge/Chromium 安装与 loopback CDP 就绪状态;绝不启动浏览器或读取 Profile |
| BrowserExternalOpen | 同一 session 的 probe 返回 `ready` 后,在精确外部浏览器中可见打开一个 HTTP(S) URL不使用 shell、Python 或 Playwright |
| BrowserSnapshot | `semantic` / `text` / `accessibility` / `visual` 四种快照——读页面的主通道 |
| BrowserAct | 一次调用一个受管动作:导航、输入、取文、等待、元素操作、截图…… |
| BrowserScript | **代码模式**:一段异步 JS 连续下发浏览器命令,消除逐动作往返(信任级别等同 Bash |
@@ -82,7 +83,7 @@ If any fetch fails, explicitly tell the user which URL failed and which fallback
| status | 必须采取的动作 |
|---|---|
| `ready` | 才能继续 Playwright `connect_over_cdp`,并如实说出检测到的外部浏览器;`any` 已有 ready 端口时,即使还安装了其他产品,也以该端口作为用户已准备的选择 |
| `ready` | 简单打开/导航调用 `BrowserExternalOpen`,参数使用返回的精确浏览器 id 与端口;点击、读取、提取等高级交互才继续隔离 Playwright `connect_over_cdp`如实说出检测到的外部浏览器;`any` 已有 ready 端口时,即使还安装了其他产品,也以该端口作为用户已准备的选择 |
| `browser_not_installed` | 明确说未检测到用户点名的浏览器;若有 `alternatives`,询问是否改用其中之一,**绝不自动替换** |
| `debug_port_closed` | 展示返回的 `launchCommand`,请用户启动并手工登录,然后等待并重新 probe |
| `browser_choice_required` | 没有 ready 端口且检测到多个外部浏览器;只列 id/name 并询问用户选哪个,再 probe 精确选择 |
@@ -94,7 +95,7 @@ If any fetch fails, explicitly tell the user which URL failed and which fallback
1. 用户在该外部浏览器里手工登录所需站点。
2. 外部浏览器窗口保持打开。
3. 再调用一次 `BrowserExternalProbe`;只有 `ready` 才允许尝试 CDP attach。
3. 再调用一次 `BrowserExternalProbe`;只有 `ready` 才允许调用 `BrowserExternalOpen` 或尝试高级 CDP attach。
不得用 `curl` 替代本探测:连接拒绝无法区分「未安装浏览器」与「已安装但没开调试」,普通 HTTP 服务也不能冒充 CDP。
@@ -103,9 +104,21 @@ If any fetch fails, explicitly tell the user which URL failed and which fallback
⚠️ 用 CDP attach 时**绝不能调 `browser.close()`**,那会关掉用户自己的外部浏览器;只关你开的 page。
完整配方见 [references/cdp-browser.md](references/cdp-browser.md)。
### 简单外部打开/导航:使用专用工具
当用户只要求在点名的外部浏览器中打开或导航到一个 HTTP(S) URL
1. 调用 `BrowserExternalProbe`,且必须得到 `ready`
2. 在同一 runtime session 调用 `BrowserExternalOpen`,传入 `browser: detectedBrowser.id`、probe 的精确 `port` 与 URL。
3. 这个简单动作不得调用 Bash、PowerShell、Python、pip 或 Playwright。
ready grant 有短时、一次性、精确 browser+port 绑定。宿主会在同一 CDP WebSocket 上重新验证
`Browser.getVersion`,匹配后才创建标签页。如果 open 报告端口关闭、无效或产品不匹配,重新 probe
并按结构化状态处理;不得改用 shell 重试。
### 按平台安全执行 Playwright
probe 返回 `ready` 后,创建或执行 attach 脚本前**必须先确认当前操作系统**
仅在 probe 返回 `ready`需要高级交互时,创建或执行 attach 脚本前**必须先确认当前操作系统**
1. **必须先创建或选择 DesireCore 拥有的隔离 venv**,不得先用全局解释器探测 Playwright。venv 不存在时,系统/引导 Python 只能用于执行 `-m venv`
2. 此后 import、固定版本安装、重新 import 与 attach 全部只能调用 venv 解释器;严禁裸 `python`、裸 `pip``pip --user`、全局安装或 `playwright install`。依赖缺失与浏览器 ready 是两件事:明确报告缺失,暂不 attach。
@@ -124,7 +137,9 @@ User intent
├─ **任何点名「我自己的 / 我本机的 / 外部 Chrome、Edge、Chromium」的请求**
│ └─→ 不论动词是搜索、读取、打开还是点击,都优先走 L3-external
│ BrowserExternalProbe精确点名产品仅 `ready` 后 connect_over_cdp()
│ BrowserExternalProbe精确点名产品仅 `ready` 后
│ 简单打开/导航 → BrowserExternalOpen精确 detected browser + port + URL
│ 高级搜索/读取/点击/提取 → 隔离 Playwright connect_over_cdp()
│ 否则按状态提示并等待;绝不能改走 WebSearch、WebFetch、Jina 或内置浏览器
├─ 只说「本地浏览器」,没有说明内置还是外部
@@ -169,7 +184,7 @@ DesireCore 能驱动**两个**浏览器,它们是平级的选项:
| | L3 内置受管浏览器 | L3-external 用户自己的浏览器 |
|---|---|---|
| 是什么 | 应用内的浏览器实例(`Browser*` 工具 | 用户点名的 Chrome/Edge/Chromium,经 CDP + Python Playwright 接管 |
| 是什么 | 应用内的浏览器实例(内置受管浏览器工具) | 用户点名的 Chrome/Edge/Chromium;简单打开走受治理 CDP 工具,高级交互才经隔离 Python Playwright 接管 |
| 登录态 | 独立隔离;需 Host 授予 `browser.import.*` 才能用 `BrowserImport` 导 Cookie | **就是用户本人的登录态**,无需导入 |
| 用户能看到吗 | Agent 开的标签页默认离屏,需展示到工作台 | **就在用户自己的窗口里**,他能全程看着、随时接管 |
| 前置条件 | 无 | `BrowserExternalProbe` 必须返回 `ready`;否则严格按结构化状态处理 |
@@ -204,7 +219,7 @@ DesireCore 能驱动**两个**浏览器,它们是平级的选项:
| L1 | Public, static | `WebFetch` | Low |
| L2 | JS-heavy, long articles, token savings | `Bash curl r.jina.ai` | **Lowest** (Markdown pre-cleaned) |
| **L3** | **登录态导航、交互与取文 (PRIMARY)** | **内置受管浏览器BrowserManage / BrowserAct / BrowserSnapshot / BrowserScript** | Medium |
| L3-external | **用户点名要用他自己的浏览器**,或 Agent 解释原因后用户明确同意改走此路径 | `BrowserExternalProbe` → 平台原生 shellWindows 必须 PowerShell+ DesireCore 隔离 venv + Playwright `connect_over_cdp`(见 references/cdp-browser.md | Medium |
| L3-external | **用户点名要用他自己的浏览器**,或 Agent 解释原因后用户明确同意改走此路径 | 简单打开/导航:`BrowserExternalProbe` `BrowserExternalOpen`高级交互ready probe → 平台原生 shellWindows 必须 PowerShell+ DesireCore 隔离 venv + Playwright `connect_over_cdp`(见 references/cdp-browser.md | Medium |
**Default priority**: L1 for simple public pages → L2 for heavy → **L3 for login-gated含正文与站内接口取数**

View File

@@ -1,5 +1,10 @@
# 内置受管浏览器工具速查L3
> **范围**:本文只描述 DesireCore **内置**受管浏览器,所以 v10.0.98+ 是本层基础能力的最低版本,
> 不是整个 web-access v3.4.3 的兼容门槛。用户点名自己的外部 Chrome/Edge/Chromium 时需要
> 客户端 v10.0.128+;简单打开走 `BrowserExternalProbe → BrowserExternalOpen`,高级 CDP/Playwright
> 交互见 [cdp-browser.md](cdp-browser.md)。不要把本文的 BrowserManage/Act 路线用于冒充外部浏览器。
>
> v2.1 起本层从「cdp-proxy 驱动用户自己的 Chrome」改为「DesireCore 内置受管浏览器」v3.0 补全能力面(批量取文 / 元素操作 / 等待 / 代码模式)并移除 Python Playwright 回落。
> 每个任务跑在独立 BrowserSpace 里Cookie / Storage / 缓存互不串扰),每个动作都经过
> Capability → Grant → Lease → Origin → Host fencing 校验并留下可审计回执。

View File

@@ -2,7 +2,9 @@
Detailed recipes for accessing sites through the user-approved external Chromium browser, via Chrome DevTools Protocol (CDP) + Python Playwright.
**Precondition**: `BrowserExternalProbe` has returned `ready` for the exact browser the user requested, that browser is running with its DesireCore-isolated profile, and the user has manually logged in to the target sites. Never infer readiness from a failed/successful `curl`; see the main SKILL.md status table.
**Scope**: this manual is only for advanced external-browser interaction such as clicking, reading dynamic content, extraction, or form work. A simple open/navigation must use `BrowserExternalProbe``BrowserExternalOpen` and must not invoke shell, Python, pip, or Playwright.
**Precondition for advanced interaction**: `BrowserExternalProbe` has returned `ready` for the exact browser the user requested, that browser is running with its DesireCore-isolated profile, and the user has manually logged in to the target sites. Never infer readiness from a failed/successful `curl`; see the main SKILL.md status table.
The probe distinguishes these cases before Playwright is involved:
@@ -13,7 +15,7 @@ The probe distinguishes these cases before Playwright is involved:
- a non-CDP service owns the port (`invalid_cdp_endpoint`)
- desktop host cannot be inspected (`host_unavailable`)
Only `ready` permits `connect_over_cdp`. An alternative browser is a suggestion requiring user approval, never an automatic fallback.
Only `ready` permits `BrowserExternalOpen` or advanced `connect_over_cdp`. An alternative browser is a suggestion requiring user approval, never an automatic fallback.
---