dsh-file-fix:给 DSH Web 补上统一文件导入

前言

如果你在 DSH Web 上试过传文件,大概率遇到过这些问题:拖入非图片文件,界面提示「仅支持 PNG、JPG、WebP、GIF」;拖错之后「拖入图片」的遮罩卡住不消失;输入框没有文件选择按钮;Ctrl+V 粘贴文件没有反应。就算文件已经在服务器上,agent 也拿不到任何文件清单,只能反复猜路径——README 里记录过一个例子,agent 一路猜了 75 步。

dsh-file-fix 解决的就是这条链路。它把文件导入统一成一种方式:任意后缀的文件,拖入、粘贴或点击选择,字节直接上传到附件库,文件清单随消息注入模型上下文,历史消息下方显示可下载的文件气泡。下面介绍它的定位、实现与安装方法。

这是什么

dsh-file-fix(v0.4.0)是 re-ITRT 维护的 DeepSeek Harness(DSH)插件,client 端目标平台为 web(dsh.client.platformweb)。一句话定位:DSH 上传体验优化插件,提供统一的文件导入体系——上传入库、清单进上下文、历史可下载、agent 可读取或导出,文件链路完全不使用 DSH 官方图片导入链路。

工作方式:从拖入到注入

插件处理一个文件分三步。

1、上传。文件字节通过 filefix/persistFile 接口写入 host 侧的内容寻址附件库 ~/.dsh/attachments/filefix/,以 sha256 去重,manifest.jsonl 做索引。附件库与工作区完全解耦,不依赖文件在磁盘上的路径,因此服务器部署的 DSH 也能用。

2、注入。发送消息时,插件通过 agent/pre-step 注入一条文件清单消息(role=user,来源标记为 plugin,notice 表单)。界面上只显示「📎 附件 N 个文件」一行摘要,模型读到的则是完整清单和每个文件的 attachment_id

3、读取或导出。模型拿到 attachment_id 后,用下面两个工具处理内容。

模型侧的两个工具

read_attachmentattachment_id 读取附件内容,支持分段:offset / limit / more,默认每段 48 KB。大文件会自动镜像一份完整副本到工作区 .dsh-uploadux/reads/ 目录,配合官方 read 工具读取全量。48 KB 这个默认值是为了避开 win32 上 dsh spill-policy 的 50 KB 内联阈值,后文注意事项会展开。

place_attachment 把附件字节导出到会话工作区的任意路径,带边界校验,防止 ../ 逃逸出工作区。

历史消息的文件气泡

插件用 filefix/files 会话事件(ignorable)记录「消息 ↔ 文件」的关联。客户端注册官方 Conversation Node(filefix-files Definition + keyed renderer),在对应文字气泡下方渲染文件列表:文件名、大小、下载链接。下载走 /plugins/dsh-file-fix/download/<attachmentId> 路由。

两层拖放 UI 与视觉上下文标记

输入框上方是两层横向列表:

  • 图像层:图片拖到这里,走 DSH 官方图片注入链路,直接进模型上下文;
  • 文件层:任意文件拖到这里,走插件的文件链路(字节入附件库 + 清单注入)。

拖到空白区时按文件类型自动分流:图片进图像层,其余进文件层。

视觉上下文标记:session 一旦包含任何直接图片注入(draft image 提交、read_image / add_image_to_context 的调用结果),就标记为「需要视觉」。此时模型选择器中不支持图片输入的模型置灰不可选;从不需要视觉的 session 切走则无限制。visual_assist(返回文本)不触发标记。

交互与实现结构

交互参照 Hermes 风格:

  • 统一 rail 混排,缩略图走降采样队列;
  • chip 三态:上传中 / 完成 / 失败,失败可点击重试;
  • 删除 chip 连带删除附件;
  • Esc 取消拖拽,drop 后焦点回到输入框。

实现分两侧:

  • host 侧:filefix Typert Remote 服务,方法包括 persistFile / limits / removeFile / markPending / unmarkPending / listFiles / checkAvailable,外加清理与视觉配置的 RPC 和下载路由;附件库、桥(session 事件监听 → 关联表 + pre-step 注入)与两个模型工具也在这侧。
  • client 侧:document 级 drop/paste 拦截(捕获阶段)、rail、📎 选择按钮、文件气泡渲染,以及设置页(视觉辅助 / 附件清理)。

安装与启用

推荐走 npm 官方渠道:

dsh plugin --profile web add dsh-file-fix

装完重启 dsh web 即生效,输入框会出现「上传文件」按钮。

卸载:

dsh plugin --profile web remove dsh-file-fix

依赖与 bundles 登记随卸载自动移除;重装再跑一次 add 命令即可全部恢复。README 称这套生命周期在干净 profile 上实测通过。

一个已知问题:pnpm 10+ 首次 add 可能报 [ERR_PNPM_IGNORED_BUILDS]——dsh 官方依赖的原生模块构建被 pnpm 拦截,任何插件都会遇到。此时再跑一次 add 即可。

从源码构建与开发

先构建,再挂载到 profile:

npm install        # 或 pnpm install(package-lock 已提交)
npm run build      # host 侧 tsc 编译到 lib/,esbuild 打包 dist/client.js
node scripts/mk-junction.cjs node_modules "<你的 dsh profile>/node_modules"
node scripts/mk-junction.cjs "<你的 dsh profile>/web/node_modules/dsh-file-fix" "$PWD"

然后在 profile 的 cordis.patch.yml 里加载本插件,可参照仓库里的 cordis.dev.yml。插件依赖 @deepseek-ai/cordis ^4.0.1、一组 @deepseek-ai/dsh-* ^0.1.1-rc.2 包和 react ^18.2.0,完整列表见 package.json 的 peerDependencies。

开发循环在 deepseek-harness 目录跑:

pnpm dsh web --patch ../dsh-file-fix/cordis.dev.yml --port 3081

host 侧改动:npm run build 后重启 dsh(lib/ 是包入口);client 侧改动:npm run build 重建 dist/client.js 后刷新页面。类型检查用 npm run typecheck,覆盖 host 与 client。

限制与注意事项

  • 大小限制的默认值为单文件 50 MB、每批 20 个、批量总量 200 MB,可通过插件 config 覆盖;超限时整批拒绝并提示。
  • win32 平台:dsh spill-policy 阈值 50 KB,纯文本工具结果超限会被替换为「头尾预览 + spill 路径」,而 spill 路径是 Windows 路径,agent 的 bash 是 Linux 语义,读不了。插件以 48 KB 默认分段加工作区镜像规避这个问题。
  • 工具集没有 shell 执行能力时,agent 无法解压或运行导出的文件,这是环境限制,不是插件问题。
  • 日志前缀 [dsh-file-fix],失败带 code:TOO_LARGE / EMPTY / SESSION_NOT_FOUND / NO_WORKSPACE / WRITE_FAILED / INVALID_PATH / REMOVE_FAILED
  • 仓库附带 scripts/repair-sessions.mjs 会话日志修复工具,流程为帧级 zstd 解压、清洗、再重压。

最后提醒一点:插件以当前 dsh 进程的权限运行,安装前建议先到仓库过一遍源码,并确认许可证信息是否符合你的使用要求。

小结

经过上面的步骤,DSH Web 的文件导入从「只收图片」补齐为「任意后缀文件入库 + 清单进上下文 + 历史可下载」,用户和 agent 看到的是同一份文件状态。仓库地址:https://github.com/re-ITRT/dsh-file-fix ;社区目录(独立站点,与 DeepSeek、幻方无官方从属关系)的收录页:https://www.skillhub.cn/plugins/re-ITRT/dsh-file-fix 。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜