Skip to content

[dsh-plugin] browser_screenshot 附件格式硬编码为 image/png 导致模型请求必败;PiP 观察窗口无关闭入口 #153

Description

@zztmly

环境信息

DSH Desktop 2.0.3(Windows 11)
DeepSeek Harness web UI(desktop-hosted)
插件 @wxg-prc-cpg/browser-skill-dsh-plugin v0.1.1(经 dshmarket 安装到 web profile)
上游 本仓库 packages/dsh-plugin-browserskill
BrowserSkill (bsk) CLI + 浏览器扩展(已连接)

Bug 1:browser_screenshot 100% 复现——截图字节被硬编码为 image/png 存库,毒死整个会话

现象

AI 调用 browser_screenshot 后:

  1. 工具结果返回一条图片附件引用:{ mediaType: "image/png", bytes: 87349, width: 820, height: 582 }(第二次测试:bytes: 82935);
  2. 但实际存入 DSH 附件库的字节是 JPEG(magic FF D8 FF DB),解析出的真实尺寸是 52×70(第二次 108×48)——与引用完全不符;
  3. 该引用进入会话历史后,任何包含它的模型请求(包括后续轮次)都在附件解析阶段失败,DSH 将其包装为 DeepSeek API stream from https://api.deepseek.com failed (code: TRANSPORT),重试策略 5/5 全部失败(每次约 0.5s 内即失败),会话从此无法继续——失败是确定性的,与网络无关(同一时刻其它会话的模型请求均正常)。

两次独立测试均复现(一次"任务完成时收尾截图",一次"任意任务中截图"),概率 100%。

代码定位(lib/index.mjs

  • trySaveScreenshot()(约 L906–L917):保存时硬编码 mediaType: "image/png",完全不校验 bsk screenshot 实际返回的字节格式:
return await attachments.saveImage({ data, mediaType: "image/png", name });
  • browser_screenshot 工具(约 L1738–L1770):
    • 临时文件扩展名硬编码 .pngbsk-screenshot-${Date.now()}-...png--out 也是 .png);
    • 返回给模型的图片引用同样硬编码 mediaType: "image/png",且 width/height 直接取 reply.width/height(bsk 报告的视口尺寸),与实际字节无关。

复现步骤

  1. 安装 @wxg-prc-cpg/browser-skill-dsh-plugin@0.1.1,确保 bsk CLI + 扩展连接正常;
  2. 任意 DSH 会话中让模型执行一个浏览器任务并调用 browser_screenshot
  3. 观察:截图工具返回后,下一次模型请求立即出现 DeepSeek API stream from https://api.deepseek.com failed(TRANSPORT),重试 5/5 失败;之后所有轮次均失败。

证据

  • 会话记录(zstd 分帧 jsonl):两次调用后的 tool/result 分别引用 sha256:79d3e829…(声称 image/png 820×582)和 sha256:4228a140…(声称 image/png);各自紧随其后的 finish: error …TRANSPORT × 6(首次 + 5 次重试)。
  • 附件库实际字节:
    • 79d3e829…:JPEG(FF D8 FF DB),87,349 B,SOF0 52×70
    • 4228a140…:JPEG(FF D8 FF DB),82,935 B,SOF0 108×48
  • DSH 侧告警:AttachmentError: Stored attachment metadata does not match its reference.dsh-attachment-localreadImageFile 校验输出)。

期望 / 修复建议

  1. trySaveScreenshot / browser_screenshot:对 bsk 输出的实际字节做格式探测(magic / 解码结果),以探测值写入 mediaType/width/height;无法确认时不要附加图片(降级为仅返回路径);
  2. 建议 bsk 侧一并确认:bsk screenshot --out x.png 输出的就是 PNG(本次观察到输出为 JPEG 且是 52×70 / 108×48 的小图,与视口 820×582 不符,疑似输出了缩略图/预览帧而非全尺寸截图)——若 bsk 端输出格式可控,请保证与扩展名一致。

Bug 2:PiP 观察预览窗口("browser unavailable")弹出后无法关闭,会话失败后卡死

现象

  1. 浏览器任务运行期间,插件会显示一个浏览器观察悬浮层(ObservationOverlay);
  2. 点击其中的 "Pop out into a mini window" 后,以浏览器 Document Picture-in-Picture 打开独立小窗,实时显示浏览器缩略帧;
  3. 会话模型请求失败(见 Bug 1)后,该小窗变为:标题栏红色圆点 + "browser unavailable",内容区保留最后一帧("last frame kept");预览框卡死且没有任何关闭入口——没有关闭按钮、点击无效、独立于 DSH 主窗口(关闭/刷新主界面也关不掉),只能整体重启。

代码定位(lib/client.cjs

  • pipApi() / popOut()(约 L3564 / L3907–L3919):使用 documentPictureInPicture.requestWindow({ width, height }) 创建窗口,随后 cloneStylesInto(win),仅监听 win.addEventListener("pagehide", …) 清理;
  • 核心问题:弹出 PiP 后所有关闭/退回入口都被置空(约 L3930–L3932):
onPopOut:    pipWindow === null && pipApi() !== void 0 ? () => void popOut() : void 0,
onCollapse:  pipWindow === null ? () => setCollapsed(true) : void 0,
onHeaderPointerDown: pipWindow === null ? beginMove : void 0

即 PiP 窗口内没有任何关闭(win.close())或返回浮动卡片的按钮

  • 会话失败 / bsk 观察通道断开后,store 不再更新(available=false → "browser unavailable",thumb 停在 "last frame kept"),窗口渲染停摆、看起来"卡死"。

复现步骤

  1. 运行任意浏览器任务,出现 ObservationOverlay;
  2. 点击 "Pop out into a mini window";
  3. 令会话失败(例如触发 Bug 1 的 browser_screenshot);
  4. 观察:PiP 小窗显示 "browser unavailable" + 保留最后一帧,无法通过任何 UI 操作关闭。

期望 / 修复建议

  1. PiP 窗口内提供关闭按钮(调用 pipWindow.close())或"回到浮动卡片"入口,不要只在 pipWindow === null 时展示操作;
  2. available=false / 会话 dead 状态下:显示可关闭/重连的状态,而非停留在不可操作的"last frame kept";
  3. 建议确认 Document PiP 窗口生命周期与主窗口的关系(主窗口关闭时是否随之销毁;当前表现为独立残留)。

附件:本地证据速查

证据 内容
附件对象 1 sha256:79d3e8293c1746d1710b8b87c2a62ae99012b2c0a27fce1351ed379597978c9f(JPEG 52×70,被标为 PNG 820×582)
附件对象 2 sha256:4228a140df28101dd47c80b96db7a9b4459ea2a17d4edb494ee104abef48e804(JPEG 108×48,被标为 PNG)
插件版本 @wxg-prc-cpg/browser-skill-dsh-plugin@0.1.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions