fix: open browser popup links in managed tabs

Links requesting a new window were silently blocked. Open them in managed browser tabs with the existing session restrictions, clean up script-closed tabs, and follow click-created tabs in subsequent direct and MCP browser tools.

Validation: 232 targeted tests and Windows Electron E2E passed; typecheck and lint passed. The full suite retains six failures outside the popup path.

Release note: Browser links that open a new page now open in a managed tab, and AI browser actions follow the new page after a click.
This commit is contained in:
mesalogo
2026-09-15 23:55:52 +08:00
parent 625182c3dc
commit f1d7197480
11 changed files with 450 additions and 20 deletions
@@ -153,9 +153,38 @@ Conversation 归属,不新建租约或把页面重新归属到子级对话。
单次工具取消信号、Tab 使用租约信号和请求信号,取消一次工具调用不终止后续调用。
该阶段 MCP transport、endpoint 和 token 生命周期不变,只扩展服务端 capability 内容和内部
路由。工具调用使用当前请求绑定的 Tab,只有上述显式导航恢复会更换绑定。
路由。工具调用使用当前请求绑定的 Tab;显式导航恢复和下述点击开窗会更换绑定。
### 6.2 Agent 多 Tab 阶段
### 6.2 Page-created tabs
`target="_blank"` and `window.open` create managed `WebContentsView` tabs through Electron's
`setWindowOpenHandler.createWindow`. Chromium retains the original request and opener relationship,
including a blank window followed by a script-assigned location. These tabs share the Conversation
partition, proxy and permission restrictions. HTTP(S) URL checks remain in the session and filtering
proxy; `about:blank` is permitted only as the initial empty popup. Unsupported URL schemes remain denied.
BrowserService reserves capacity before creating the native view, assigns a new Tab/workbar identity,
and owns initialization, the Driver and cleanup. Initialization failure or Conversation disposal releases
the popup. Closing the opener leaves an already created popup alive. A popup's `window.close()` releases
its service slot and usage leases; Renderer removes its workbar instance and returns to the opener when
it is still available. No separate unmanaged BrowserWindow is created.
Live state includes `openerTabId`. A popup from the visible browser is selected even when the active chat
has switched to a different Conversation. Other workbar panels and viewport occlusion rules retain their
existing behavior. The existing workbar capacity notification still applies when all 32 slots are occupied.
During `browser_click`, the service collects popup registrations from that Tab and waits for their
initialization. The result identifies the last created popup. BrowserModelTools binds subsequent tools to
that Tab and asks the model to obtain new references with `browser_snapshot`; the MCP gateway also replaces
its capability binding and usage lease. Switching visible tabs does not alter this binding. Closing the
popup retains the existing explicit `browser_navigate` recovery behavior. A page may still be loading
when the click returns. Popups requested asynchronously after the click has completed are displayed as
managed tabs, but are not attributed to that completed tool call.
The remote ACP Runtime exposes its separately scoped image MCP server, not this desktop browser gateway;
this change does not add browser access to the deployed GoodBuddy Agent.
### 6.3 Agent 多 Tab 阶段
只有明确需要 Agent 管理多个页面时,才增加:
+7 -1
View File
@@ -230,7 +230,7 @@ Runtime
从当前聊天右栏迁移时,Task Center 和工作区作为默认打开、单实例且不可关闭的基础应用保留;
浏览器保留一个默认打开实例,但注册为可关闭的多实例应用;成果保持默认打开、单实例且可
关闭;终端不默认打开,由用户通过“+”创建,并从首期开始支持多实例。Tab 必须单行横向滚动,
不能自动隐藏或缩写到不可辨认,“+”不能随滚动消失。Task Center 继续作为 Task 的现有
不能自动隐藏或缩写到不可辨认,“+”紧随最后一个 Tab 并随标签滚动,溢出时两端提供固定滚动箭头。Task Center 继续作为 Task 的现有
入口;审批定位到所属 Task 或 Runtime。智能心跳不作为工作栏页签,其报告、建议、历史和
完整配置统一归属“智能心跳”菜单入口。当前阶段不新增独立自动化中心。
@@ -457,6 +457,12 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
### 7.7 浏览器
Page requests made with `target="_blank"` or `window.open` open managed browser tabs in the same
Conversation. A popup from the visible page becomes visible in the workbar; closing it from page script
removes that popup tab. When an Agent click opens a new tab, subsequent browser tools follow that page
and obtain fresh element references. Detailed lifecycle and routing rules are defined in
[Page-created tabs](./browser-tabs-technical-design.md#62-page-created-tabs).
浏览器是可关闭的多实例应用。升级后的默认布局保留一个浏览器实例;用户从“+”目录选择
浏览器时每次创建新的 Browser Tab。每个实例必须显示可区分的标题和绑定 Conversation,
不能让多个工作栏 Tab 只是同一页面的重复控制入口。
@@ -1,5 +1,32 @@
# 工作栏实现与验证进度
## 2026-09-15 Managed browser popups
Implemented [page-created tabs](./browser-tabs-technical-design.md#62-page-created-tabs): native
Electron popup creation now registers a managed Tab, and click results update direct-tool and MCP
bindings. Renderer selects a visible opener's popup across chat switches and removes script-closed
popup instances. Existing worktree changes were retained.
Validation against the current source:
- `npx vitest run src/main/browser src/main/agent/knowledge-mcp-gateway.test.ts src/main/agent/model-tool-provider.test.ts src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/contracts.test.ts`: 10 files, 232 tests passed.
- `node build/run-browser-tabs-electron-e2e.cjs`: passed twice after making the test wait for the shown
native view's animation frames before sending mouse input. Real Chromium verified `target=_blank`,
`window.open(url)`, blank-then-location, script close, opener close, shared partition, unsupported URL
denial, and subsequent direct/MCP snapshots on the popup. No fixed click delay was added to production.
- `npm run typecheck` and `npm run lint`: passed. `git diff --check`: no whitespace errors.
- `npm test`: 4319 passed, 67 skipped, 6 failed across 5 files. Failures were Agent offline dependency
installation timeout, local Runtime reuse timeout, diagnostic-log writer expectations, two heartbeat
migration fixtures with `duplicate column name: pinned`, and an App external knowledge binding test
missing the `Remote handbook` heading. The full suite was not green; none of these failures exercise
the popup registration or binding path.
The Electron test uses production BrowserService, CDP and MCP with a local HTTP fixture; the workbar
selection/removal path is covered by the actual Renderer component test. A complete installed-App
visual test and macOS/Linux native popup checks were not run. No external model calls were made for
the popup validation. Remote ACP exposes only its scoped image MCP server (`imageMcpServers`), so the
deployed Agent has no separate affected browser implementation and no remote Host change was needed.
## 2026-09-15 会话置顶与浮动操作菜单
已核对当前源码差异:置顶经专用 IPC 写入 SQLite,schema 36 迁移保留已有会话;会话操作