DeepSeek Harness plugin

dsh-image-unlock

Lift the 'model does not support images' gate in the DeepSeek Harness web GUI: declares image input for every llm-pi-ai model that does not state its own input modalities, so attachments are

Jump to install

Source facts

Repository
FrostLeafKEE/dsh-image-unlock
Latest update
Aug 15, 2026
Category
Security & Permissions
GitHub stars
1
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/FrostLeafKEE/dsh-image-unlock
Plugin: dsh-image-unlock
Author: FrostLeafKEE

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer3 files
README.mdSource · read only

🐳 dsh-image-unlock

<p align="center"> <img src="assets/hero-banner.jpg" alt="鲸鱼娘为你解锁图片输入" width="100%"> </p>

<div align="center">

解除 DeepSeek Harness Web GUI 的图片输入限制,让图片附件在不支持图片的上游(Console Go)下可用。

![version]() ![license]() ![platform]() ![whale girl approved]()

</div>

---

✨ 一张图看懂

 🖼️ 上传图片
   │
   ▼
 🚪 准入闸门   ←── bundle patch:defaultInput: [text, image]
   │               (未声明 input 的模型继承图片能力,不再被拒)
   ▼
 🔧 图片文本化  ←── code patch:textifyImageBlocks()
   │               (图片块 → [图片附件: <路径>] 文本标记)
   ▼
 🤖 agent 看到标记 → 👁️ vision skill 识图(vision.js 魔数嗅探 MIME)

---

🧩 问题

Harness 在 API 层(dsh-host-apiproxy)、LLM 层(dsh-llm-pi-ai)都根据模型的 inputModalities(来自 llm-pi-ai 的模型 input 声明)判断能否接收图片。 默认声明只有 text,因此未声明 input 的模型(如 deepseek-v4-flash)在发图时 会被拒绝("Model does not support image input")。

但只放行还不够:dsh-llm-pi-ai 会把 durable 图片块转成 { type: "image", data: base64 } 部件,pi-ai SDK 序列化为 image_url 发送给 上游。Console Go 上游协议只接受 text,于是报:

400 invalid_request_error: Failed to deserialize the JSON body into the
target type: messages[605]: unknown variant `image_url`, expected `text`

也就是说:声明图片能力是"准入",但上游根本不收图片。正确做法是准入后把 图片块转成带附件路径的文本标记,识别交给 vision skill。

---

⚙️ 方案(两部分)

1. Bundle patch:准入(cordis.patch.yml

llm-pi-aiopencode-go provider 加上 defaultInput: [text, image], 让未显式声明 input 的模型继承 image 输入能力,从而通过 API 层 / LLM 层的 图片闸门。显式声明 input: [text] 的模型保持原样(用户层优先)。

2. 代码 patch:图片文本化(scripts/patch-pi-ai.mjs

dsh-llm-pi-ai 的序列化没有配置开关,请求对象又是深度冻结的(llm/stream waterfall 无法改写),所以对本地安装的包做一次代码补丁(已用 [dsh-plugin-image-unlock] 标记注释标注):

  • stream() 中,对 opencode-go 路由先调用 textifyImageBlocks()

把 durable 图片块(含 tool-result 内嵌套的)替换为文本标记;是否执行由 运行时开关 globalThis.__dsh_image_unlock_textify 控制(见下节)。

`` [图片附件: <名称>] 附件已保存到 <DSH_HOME>/attachments/v1/objects/<xx>/<sha256>(当前模型不能直接查看图片,请用 claude-vision-skill 识别该路径;无法访问时用 --clipboard 回退) ``

  • 附件对象文件没有扩展名,claude-vision-skill/vision.js 已支持按魔数嗅探

MIME(png/jpeg/webp/gif),可直接识读该路径;剪贴板回退仍然可用。

这样:上传不再被拒(第 1 部分)、请求不再 400(第 2 部分)、agent 看到 [图片附件: ...] 标记后按 vision skill 流程识别(配套 skill 已更新文档)。

---

📦 安装

方式一:本地目录(开发调试用)

dsh plugin --profile web add <本插件目录>

方式二:从 GitHub 直接安装

dsh plugin --profile web add https://github.com/FrostLeafKEE/dsh-image-unlock.git

> dsh plugin 会把参数转发给 profile 目录里的 pnpm,两种方式等价;安装后 > dsh.profile.bundles 会自动登记 @dsh-external/dsh-image-unlock

安装后还需要在 profile 的 cordis.patch.yml 中插入插件条目(见下节), 并打一次代码补丁(见"重装/升级后重新打补丁")。

---

🎛️ 开关(skill 驱动:禁用 skill = 原生视觉模型)

开关与 claude-vision-skill 的启停绑定,全部即时生效、无需重启:

  • 启用 skill(默认)claude-vision-skill 目录在 .agents/skills 下 →

文本化开启,图片转成文本标记,agent 走 vision.js 识图流程 (适合当前 text-only 的 opencode-go 模型)。

  • 禁用 skill(用原生视觉模型):把 <工作区>/.agents/skills/claude-vision-skill

目录移出(例如移到 <工作区>/.dshskin/ 下)→ 文本化自动关闭, 模型收到真实图片部件。想切回 vision-skill 流程就放回原处。

插件会监视 skills 目录,目录移动即刻切换,无需重启。

附加覆盖(profile 的 cordis.patch.yml 插件条目):

- insert:
    - id: image-unlock
      name: '@dsh-external/dsh-image-unlock'
      # disabled: true            # 整体关闭:取消注释这一行
      config:
        textifyImages: true       # 强制关文本化:改成 false(即使 skill 在)
        # skillDir: '<绝对路径>'  # 自定义 skill 目录位置(默认自动探测)

> 注意:bundle patch 的 defaultInput 声明不受上述开关影响(只影响未声明 > input 的模型;视觉模型应通过 settings 声明自己的 input: [text, image], > 其声明优先于 defaultInput)。

运行时标志:插件把开关写入 globalThis.__dsh_image_unlock_textifydsh-llm-pi-ai 补丁代码在每次请求时读取它(=== true 才文本化)。条目被 禁用、插件被移除、或 skill 目录不存在时标志被清除,文本化随之关闭。

---

🔄 重装/升级后重新打补丁

第 2 部分是针对 node_modules 的代码补丁,pnpm install / 升级 @deepseek-ai/dsh 会把它冲掉。重装后执行(幂等,已打补丁会直接跳过):

node scripts/patch-pi-ai.mjs

默认在以下位置寻找目标文件(也可用参数指定绝对路径):

  • <本插件目录>/../../DeepSeekHarness/node_modules/@deepseek-ai/dsh-llm-pi-ai/lib/index.js
  • <本插件目录>/../DeepSeekHarness/node_modules/@deepseek-ai/dsh-llm-pi-ai/lib/index.js
  • <本插件目录>/../../../node_modules/@deepseek-ai/dsh-llm-pi-ai/lib/index.js

---

⚠️ 注意

  • 这只放行图片附件并文本化;模型本身不支持图像输入。实际识图请配合

vision skill(claude-vision-skill,通过 vision.js 调用外部 vision API)。

  • 文本化只影响 opencode-go 路由,其它 pi-ai 路由行为不变。
  • 若某天 opencode-go 路由真的挂上了原生视觉模型,用上面的开关关闭文本化即可;

彻底移除的话:删掉插件条目、dsh plugin remove、并手动删除 dsh-llm-pi-ai/lib/index.js 中两处 [dsh-plugin-image-unlock] 标记的代码。

---

<p align="center"> 🐳 头图由 <code>wan2.7-image-pro</code> 生成 · DeepSeek 鲸鱼娘风格<br> <sub>MIT License · dsh-plugin · deepseek-harness</sub> </p>