makecindy / makecindy/cindy

bug: 内置浏览器截图链路:截图后 tab 会话丢失 + 间歇 UnknownVizError + 截图无法落盘 + jpeg 格式参数被静默忽略

Open
#1,876 1 comment 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

### 问题描述 / What happened

浏览器自动化(内置浏览器 / rsb-webview 运行时)的截图链路存在多个相互关联的问题,导致「截图」这一基本操作不可可靠使用:

1. **截图调用后 tab 从 active session 掉出**:`previewLocalHtml` / `open` 返回的 `targetId` 在后续调用中失效,报 `tab not in active session `。随后 `tabs` 为空,但 `status` 仍显示 `ready: true` 且 `tabCount: 0`。
- **对照实验发现强关联**:只要调用过一次截图(无论成功还是失败),下一次操作必定报 tab 不在 active session;而不调用截图、只做 `snapshot`,连续 8 次 tab 保持稳定。
- 掉出的 tab **并未被销毁**:稍后 `tabs` 列表重新显示这些 tab 仍存在。问题在「active session 映射」层,不在 tab 生命周期。
2. **截图可视化层间歇失败**:默认截图和显式 `type: png` 都出现 `BROWSER_RUNTIME_ACTION_FAILED` + `UnknownVizError`。重试一次仍失败。tab 本身存活(`snapshot` 可用),与 tab 生命周期无关。
3. **截图结果无法可靠交付为文件**:截图可成功返回内嵌 base64,但:
- 接口没有文件输出参数(传 `path` 被拒 `unrecognized_keys`);
- `saveResource` 只接收 `snapshot(urls: true)` 返回的 http(s) 资源地址,不能保存整页截图(对本地 http 资源也被拒);
- 成功截图未自动落盘(媒体目录无新文件)。
- 在部分宿主(Codex 桌面端)中,长 base64 在宿主输出层被截断(`…22 chars truncated…`),无法还原为图片文件。
4. **格式参数语义不一致**:请求 `type: "jpeg"` 后调用成功,但返回仍为 `image/png`,base64 以 PNG 魔数开头,字节数与先前 PNG 请求**完全相同**(均为 11050 字节)。`type` 参数被静默忽略或响应被缓存。

### 环境 / Environment

- Cindy 版本或 commit:**0.1.33**(本地自构建开发版,内置浏览器,`backend: rsb-webview`)
- 平台与 OS 版本:Windows 10 Pro 10.0.19045
- 安装方式:本地构建的开发版;复测通道:Claude Code + Cindy 内置浏览器(`cindy_browser` MCP,`backend: rsb-webview`);现象与 Codex 桌面端内置浏览器一致

### 复现步骤 / Steps to reproduce

测试页:`dist/index.html`(标题 `2A Preview Test`,脚本将 `hello from 2A` 改为 `js-ran`,含 `pixel.png` 图片;通过本地 http 服务 serve)。

**A. 截图后 tab 掉出(可稳定复现)**

1. `open` 打开测试页 → 记录返回的 `targetId`
2. `snapshot`(同 targetId)→ 成功
3. `screenshot`(同 targetId,png)→ 成功(返回 `bytes: 11050`)
4. 再次 `screenshot`(同 targetId,jpeg)→ 失败:`BROWSER_RUNTIME_ACTION_FAILED` / `tab not in active session `
5. `tabs` → 空列表;`status` → `ready: true`,`tabCount: 0`(sessionId 不变)
6. 过一段时间后 `tabs` 重新列出该 tab(tab 未销毁)

对照:不调用 screenshot,只重复 `snapshot` ×8 → tab 一直可用。

**B. 截图间歇失败**

1. 重新 `open` 测试页
2. `screenshot`(png)→ `UnknownVizError`
3. 重试一次 → 仍 `UnknownVizError`
4. `snapshot` → 成功(页面正常,tab 存活)

复测统计(单次会话):截图成功 2 次、失败 4 次(`UnknownVizError`);3/3 个 tab 在截图调用后掉出 active session。

**C. 格式参数无效**

1. `open` 测试页,`snapshot` 确认正常
2. `screenshot`(`type: "png"`)→ 成功,`mimeType: image/png`,`bytes: 11050`
3. `screenshot`(`type: "jpeg"`)→ 返回 `mimeType: image/png`,base64 以 PNG 魔数 `iVBORw0KGgo` 开头,`bytes: 11050`(与 PNG 请求完全相同)

**D. 无文件输出通道**

1. `screenshot` 传 `path` → `unrecognized_keys: ["path"]`(schema 中无此参数)
2. `act { kind: "saveResource", url: }` → `BROWSER_RUNTIME_INVALID_REQUEST`(对页面资源 URL 也被拒)
3. 截图成功后检查媒体目录 → 无新增文件

### 日志与截图 / Logs & screenshots

- 错误原文(A 步骤 4):`BROWSER_RUNTIME_ACTION_FAILED`,message: `tab t_msh30ylg_b8vuf5ck not in active session 8643d19a-5cd8-4370-a353-33b0ec8934b4`
- 错误原文(B):`BROWSER_RUNTIME_ACTION_FAILED`,message: `UnknownVizError`
- 错误原文(D 步骤 1):`INVALID_ARGS`,`unrecognized_keys: ["path"]`
- 错误原文(D 步骤 2):`BROWSER_RUNTIME_INVALID_REQUEST`,message: `saveResource.url 必须是 snapshot(urls:true) 返回的 http(s) 资源地址`
- 补充现象(部分宿主):长 base64 在宿主输出层显示 `…22 chars truncated…`;将长 base64 拼入 Shell 参数时报 `failed to parse function arguments: EOF while parsing a string`(工具参数 JSON 在字符串闭合前被截断)

### 期望行为 / Expected behavior

- 截图调用不应破坏 tab 的 active session 映射;`targetId` 应能跨调用连续完成 `snapshot` 和 `screenshot`(连续 20 次无会话丢失)。
- `UnknownVizError` 应返回关联的阶段信息和可操作的恢复建议;连续截图可量化成功率。
- 截图应有受支持的文件输出通道(如 `savePath`、短时效下载 URL、或自动落盘 + 返回路径与元数据),不依赖模型搬运 base64。
- `type: "jpeg"` 应返回真实 JPEG(MIME、扩展名、魔数一致);不支持的参数应显式报错,不能静默忽略。

### 可能的根因线索(供排查参考)

- 「截图调用(成功或失败)→ tab 掉出 active session 映射」呈强关联,且 tab 本身存活。建议排查截图路径对 session/tab 映射的副作用(例如截图后触发 tab 重新注册、会话切换或映射清理)。若截图链路与映射丢失同根,修复截图链路可能同时解决 1、2 两项。
- `type` 参数在路由层有解析(`body.type === "jpeg" ? "jpeg" : "png"`)且各截图路径(CDP / Playwright / ChromeMCP)均向下传递;未超限时归一化层原样返回 buffer 且不带 contentType。jpeg 请求返回与 png 逐字节相同的输出,疑似 `type` 在运行时被忽略或响应缓存命中。
- 截图成功但媒体目录无新文件,与「截图响应走文件路径」的既有实现不一致,疑似响应交付链路在宿主侧有另一实现或分支。

Contributor guide

Open the contributing guide

Research direction

Start with the screenshot, open, snapshot, and tabs flows in the TypeScript browser runtime using the rsb-webview backend, reproducing against dist/index.html. Compare the active-session behavior, UnknownVizError results, file-output handling, and png/jpeg metadata under the listed steps. Done means repeated screenshot calls preserve the tab session, failures provide useful recovery detail, supported output reaches a file or equivalent channel, and JPEG requests return actual JPEG data.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.