bug(desktop/codex): Playwright CLI named sessions leak orphan headless system Chrome on macOS
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 问题概述
Cindy 托管的 Codex Agent 使用 Playwright CLI 创建命名浏览器会话后,在任务完成、会话结束及 Cindy 重启时没有回收这些会话。Playwright 的 detached `cliDaemon.js` 与它启动的 headless 系统 Chrome 会跨任务、跨 Cindy 进程继续存活,最终成为 `PPID=1` 的孤儿进程。
在 macOS 上,这些进程使用系统 `/Applications/Google Chrome.app`,与用户日常 Chrome 共享 `com.google.Chrome` 应用身份。它们没有普通窗口,Dock 无法正常打开/关闭;同时可能放大 #1854 中普通外部链接被投递到自动化 Chrome 实例的问题。
这不是项目脚本或用户手动启动的浏览器:本机 Cindy Codex session rollout 明确记录了 Agent 的 Playwright CLI 工具调用,且任务正常完成后没有对应的 `close`。
## 环境
- Cindy: 0.1.50
- macOS: 26.6.1 (25G76), arm64
- Google Chrome: 151.0.7922.138
- `@playwright/cli`: 0.1.18
- `playwright-core`: 1.63.0-alpha-2026-08-05
## 本机证据与时间线
2026-08-15,Cindy 托管的 Codex 在 MarkGrove 浏览器验收中依次执行:
```text
playwright_cli.sh -s=prod2 open http://127.0.0.1:4173/
playwright_cli.sh -s=dev2 open http://127.0.0.1:5173/
playwright_cli.sh -s=prod3 open http://127.0.0.1:4173/
playwright_cli.sh -s=dragaudit open http://127.0.0.1:5173/
```
对应 rollout 中记录了各会话成功启动:
```text
Browser prod2 opened with pid 74632
Browser dev2 opened with pid 75856
Browser prod3 opened with pid 76312
Browser dragaudit opened with pid 78706
```
之后相关 turn 均正常进入 `task_complete`,但 session 记录中没有任何对应命令:
```text
-s= close
close-all
kill-all
```
到 2026-08-17,这些进程仍在:
```text
node .../playwright-core/lib/entry/cliDaemon.js prod2
node .../playwright-core/lib/entry/cliDaemon.js dev2
node .../playwright-core/lib/entry/cliDaemon.js prod3
node .../playwright-core/lib/entry/cliDaemon.js dragaudit
```
四个 daemon 均为 `PPID=1`,每个都维持一个使用临时 `playwright_chromiumdev_profile-*` 的系统 Chrome,Chrome 参数包括:
```text
--headless
--no-startup-window
--remote-debugging-pipe
--user-data-dir=.../playwright_chromiumdev_profile-...
```
Cindy 在此期间已经重启,因此这些进程已脱离原 Cindy/Codex 生命周期。
执行 Playwright 官方清理命令后:
```sh
playwright-cli close-all
playwright-cli list
```
结果变为 `(no browsers)`,所有 `cliDaemon.js` 和 `playwright_chromiumdev_profile-*` 进程消失。
## 预期行为
- Cindy/Agent 创建的 Playwright 会话在不再需要时正常关闭。
- Cindy 退出、异常终止或下一次启动时,可以识别并处理仅由 Cindy 拥有的 stale Playwright sessions。
- 不留下无窗口、无法从 Dock 管理的系统 Chrome。
- 不影响用户从其他终端、IDE 或项目自行启动的 Playwright sessions。
## 实际行为
- Agent 创建命名会话后只完成业务任务,没有配对执行 `close`。
- Playwright CLI daemon 按设计 detached,原调用方退出后继续存活。
- Cindy 没有记录会话所有权,也没有任务/应用生命周期兜底。
- macOS 看到的是仍在运行但无窗口的 Google Chrome;普通 URL 路由还可能与 #1854 叠加。
## 根因判断
Playwright CLI 的命名 session 持久化本身是公开设计;其文档明确要求调用方在完成后执行 `close` / `close-all`,僵尸状态使用 `kill-all`。因此主要缺陷不应描述为“Playwright 随机无法退出”,而是:
1. Cindy 托管 Agent 的 Playwright workflow 没有保证 open/close 配对;
2. Cindy Host 没有为 Agent 创建的 Playwright session 建立所有权与退出恢复;
3. headless 自动化使用系统 Google Chrome,使孤儿实例在 macOS 上与日常 Chrome 共享应用身份。
## 与 #1854 的关系
- #1854:Cindy 外置浏览器使用系统 Chrome + 独立 profile,普通外部 URL 可能进入 Cindy profile。
- 本 Issue:Cindy 托管 Codex 创建的 Playwright CLI 命名会话未回收,daemon/headless Chrome 跨任务、跨 Cindy 重启残留。
两者机制不同,不是重复 Issue;但都会受到 macOS 上相同 Chrome bundle identity/URL 投递行为影响,应交叉回归。
## 建议修复
不要在 Cindy 退出时直接全局执行 `playwright-cli kill-all`,这可能误杀用户在其他终端或项目启动的会话。
建议按所有权清理:
1. Cindy 为 Agent 创建的 Playwright session 使用包含 Cindy instance/dialogue/turn 标识的 session 名。
2. Host 记录本实例创建的 session 与 daemon/browser PID。
3. 正常任务结束时对临时验收会话执行对应 `close`。
4. Cindy 退出时只关闭本实例拥有的 session;崩溃后下次启动按 ownership marker 清理 stale session。
5. Cindy-side Playwright Skill 明确要求 `open` 必须与 `close` 配对,并在异常路径使用 finally-style cleanup。
6. macOS headless 自动化优先使用 Playwright Chromium/Chrome for Testing 或独立 bundle identity,避免系统日常 Chrome 的 URL routing。
7. 增加测试:正常完成、Agent error/abort、Cindy quit/restart、多个 workspace 并行、外部非 Cindy Playwright session 不被误杀。
## 验收标准
- Agent 浏览器验收结束后,Cindy 创建的命名 session 不再出现在 `playwright-cli list`。
- Cindy 重启后不存在属于上一实例的 `cliDaemon.js` / headless Chrome。
- 用户自行启动的 Playwright session 保持不受影响。
- Cindy 自动化运行期间及结束后,普通外部链接始终进入用户日常默认浏览器。
Contributor guide
Research direction
Start by tracing the Cindy-hosted Playwright CLI workflow around the recorded open commands and the playwright-core/lib/entry/cliDaemon.js process. Review how session ownership and task or application lifecycle are currently represented, then run the normal-completion, abort, quit/restart, parallel-workspace, and external-session cases described in the acceptance criteria. Done means Cindy-owned sessions are cleaned up without affecting user-owned sessions or external URL routing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, macos, playwright, typescript
- Domain
- desktop, testing-qa
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100