Back to home@rangdl

dsh-all-enhance

DSH(DeepSeek Harness)功能增强插件

Stars
0
Language
TypeScript
Created
Aug 28, 2026
Updated
Sep 1, 2026
GitHub repo

Introduction

dsh-all-enhance

npm npm downloads license node dshfind

DSH(DeepSeek Harness)功能增强插件:会话删除、模型请求头、模型回退链 —— 三大功能均可在 Web 设置页的「增强设置」分区中开关与配置。

中文 | English

快速安装

需要 dsh(DeepSeek Harness),一条命令安装到目标 profile(如 web),重启后生效:

dsh plugin --profile web add dsh-all-enhance

推荐固定稳定版本,避免后续发布悄悄改变实际运行的代码:

dsh plugin --profile web add dsh-all-enhance@latest

安装后:Web 端进入「设置 → 插件配置 / 增强设置」即可开关与配置三大功能(会话删除、模型请求头、模型回退链)。更详细的安装方式见 安装

功能

会话删除

在会话列表的更多操作菜单中新增「删除会话」:

  • 冷会话(未运行)永久删除本地记录:日志文件与工作区账目一并清理,侧栏行即时移除。
  • 运行中会话拒删:服务端 409 + TOCTOU 二次复核,绝不误删正在跑的任务。
  • 删除需二次显式确认;目录移除带会话 ID 名称护栏,防止 DSH 布局变化时误删无关目录。

模型请求头

为出站模型请求注入自定义请求头(如 User-Agent),保存在插件自己的设置命名空间(不写入 llm-pi-ai,任意渠道含官方内置都可配置):

  • 优先级:按模型(键 提供方/模型)> 按提供方 > 全局,同名请求头大小写不敏感覆盖。
  • 注入范围:对话流、「获取可用模型」探测、底层 fetch 全覆盖,经 AsyncLocalStorage 贯穿整条请求链路。
  • 内置 User-Agent 预设(claude-cli / claude-code 等)可选填,仍可自由编辑。
  • 总开关关闭时零注入;保存前校验请求头名(RFC 9110 token)与值(无换行,防 CRLF 注入)。

模型回退链

对话请求失败时自动切换到链中的下一个模型继续,无需手动换模型:

  • 触发:失败码命中触发码即回退。默认含鉴权无效 / 配额耗尽 / 限流 / 连接断开(TRANSPORT)/ 流空闲超时(TIMEOUT);流中断同样覆盖,用户主动中止不触发。
  • 多链 + 虚拟路由:每条链注册为虚拟路由,在模型选择器中显示为 EnhanceChain/<链 id>,选中即以该链为主模型;内置 auto 链自动聚合全部已注册提供方并置于链首,同时是普通模型失败时的默认回退链。
  • 链编辑:链目从模型目录下拉选择(提供方/模型,支持 * 通配)或手动输入;每条目可上下调序。链 id 支持字母开头 + . _ - 数字。
  • 稳定性:失败路由冷却抑制(定时解禁,或半开探测指数升级至 16 倍);每 step 切换次数上限防抖动。
  • 可观测:切换事件 toast 实时提示(SSE 推送,流不可用降级共享轮询);运行状态面板展示冷却快照与切换历史。
  • 调用记录(可选 fallbacksLogEnabled):记录每次失败的失败码、响应消息与回退目标(回退关闭时也记录),附失败码统计直方图,一键加入触发码便于调优;经 storageDomain 域持久化,重启不丢。
  • 回退决策状态纯内存,不写会话日志。

安装

作为带 dsh.bundle patch 层的组合包:

dsh plugin --profile <name> add dsh-all-enhance

或从源码检出(使用 pnpm dsh):

pnpm dsh plugin --profile <name> add ./dsh-all-enhance

组合包注册一条 Cordis 配置项(id: all-enhance),同时挂载 Host 半侧与 Web 客户端半侧。

本机免 pnpm 安装(pnpm 受限环境)

dsh plugin add(内部走 pnpm)不可用时,可直接手工安装(无需删除任何东西):

# 1. 把插件以包名链接进 profile 的 node_modules(junction,跟随源码改动)
#    <插件源码绝对路径> 替换为本机 dsh-all-enhance 源码检出目录
node -e "require('fs').symlinkSync('<插件源码绝对路径>', process.env.USERPROFILE + '/.dsh/profiles/web/node_modules/dsh-all-enhance', 'junction')"

# 2. 在 profile 的 cordis.patch.yml 追加(用包名,client 半侧才会被发现):
#   - insert:
#       - id: all-enhance
#         name: dsh-all-enhance

# 3. 直接启动(不要再叠加 --patch,否则 duplicate entry id)
dsh web

注意:name文件路径(如 file:///.../src/index.js)只加载 Host 半侧,客户端模块系统按"包"扫描 dsh.client 声明,路径加载时 UI 不会注入;必须用包名加载才带 UI。

工作原理 / API 面

插件尽量只使用官方公开服务接口;两处官方私有实现(attached 会话从 SessionStore 内存 detach、session_projcache 表清理)以能力探测的方式穿透,DSH 升级改结构 时自动降级为 409 拒删,不会崩溃:

关注点使用的公开 API
设置命名空间注册@deepseek-ai/dsh-settingsinstallSettingsSection()(生命周期托管)
运行中会话检测ctx.sessions.get(id)(SessionStore)
会话存在性ctx.sessionPersistence.list()
日志文件定位ctx.sessionPersistence.locate(header) —— 由后端解析物理路径,无需手写路径编码
工作区账目ctx.workspaceRegistry.list() + Workspace.detachSession(id)
HTTP 路由ctx.webServer.register()(包在 ctx.effect() 内,卸载自动注销)
设置写入(客户端)api.settings.mutate 携带 expectedRevision + 冲突重试
请求头注入自有设置命名空间 + ctx.on('llm/stream') + 作用域化 fetch / ctx.llm.discoverModels 包装(ctx.effect 托管,卸载还原)
回退决策ctx.on('agent/request-error'){ kind: 'retry' },pending 切换在 ctx.on('agent/request') 应用,per-agent 状态随 agent/disposed 清理
虚拟链路由ctx.llm.registerAdapter(['EnhanceChain'], …) —— 每条链一行 listModelsstream() 薄委托到链 head
回退运行状态GET /dsh-all-enhance/fallbacks/status(冷却快照 + 切换历史,仅内存)
回退调用记录GET/DELETE /dsh-all-enhance/fallbacks/log —— 失败观测日志(环形 200 条,storageDomain 域持久化,服务不可用降级纯内存)
模型目录GET /dsh-all-enhance/fallbacks/models —— 聚合 ctx.llm.listProviders() + listModels(),链编辑器下拉框数据源
回退状态推送GET /dsh-all-enhance/fallbacks/events(SSE)—— 切换/冷却变更时推送快照;流不可用降级共享轮询(约 5 秒,连续失败指数退避)

安全

  • 删除/状态路由仅接受本机回环请求:Host + sec-fetch-site + origin 校验拒绝跨站请求(CSRF / DNS rebinding 防护)。
  • POST body 上限 64 KiB,且必须为 application/json
  • 运行中会话绝不删除(服务端 409;客户端在弹确认框前先查状态)。
  • 冷会话删除不可恢复;UI 要求二次显式确认。
  • 请求头保存前做校验:名称必须是 RFC 9110 token,值不得含换行(防 CRLF 头注入)。

兼容性与已知限制

  • 需要服务:webServersettingsworkspaceRegistrysessionPersistencesessions
  • Web 客户端目前对设置页导航与会话列表菜单没有官方扩展 slot;客户端使用内部哈希类名(.VOzbGW_*.YDXeBa_*)与 React fiber 探测做 DOM 注入,DSH UI 升级可能需要维护选择器。可迁移的官方 slot:settings.plugin.itemconversation.chat.node
  • 归档集无公开 unarchive API(archivedSessionIds 只增不减):删除已归档会话后归档集保留失效 id。
  • 进程内 header 索引缓存无公开清理 API:删除后由重启 bootstrap 自愈。
  • locate() 只返回主日志产物;compaction snapshot 等后端附加产物不在公开接口内,不清理。
  • 模型回退链依赖当前 DSH 的 agent/request-error / agent/request 事件契约与 ctx.llm.registerAdapter;宿主升级若调整失败码或适配器注册机制可能需要插件跟进(未知失败码不会触发回退,安全降级)。
  • 流中断回退覆盖适配器层失败(连接断开、空闲超时 —— 归一为 TRANSPORT/TIMEOUT finish chunk)。llm/stream 管道内消费者/中间件层失败会完全绕过 agent/request-error,在宿主循环中即为终态 —— 任何插件都无法恢复。
  • 切换 toast 优先走 SSE 推送;流不可用时客户端降级为约 5 秒的共享轮询,通知最多延迟一个轮询周期。

参考项目

  • btspoony/dsh-llm-fallbacks(MIT)— 回退链的决策内核参考:agent/request-error 接管恢复、冷却抑制与 half-open 恢复的机制均源于此(本插件剥离了其角色/时段/subagent 策略,并对齐 dsh 0.1.1-rc.2 的失败码与事件契约;其 issue #52 的会话事件坑位也在本插件规避)。
  • dsh-custom-provider-settings(社区插件)— 请求头注入链路参考:llm/stream 事件 + AsyncLocalStorage + fetch 包装的同构实现(本插件 TS 化并改为自有设置命名空间,runtime key 亦区分,两者可共存)。
  • dsh-market — 构建方式镜像:tsc 编译 host + tsdown 打包 client、结构化服务子集类型与 bundle/patch 层组织方式。
  • deepseek-ai/deepseek-harness — 宿主框架(DSH)官方仓库。

开发

源码为 TypeScript,构建方式镜像 dsh-market (tsc 编译 host + tsdown 打包 client):

src/
├── host/                     # Host 半侧(TypeScript,tsc 编译 → lib/)
│   ├── index.ts              #   插件入口 apply(装配以下模块)
│   ├── ctx.ts                #   结构化服务子集类型(含两处私有穿透成员)
│   ├── constants.ts          #   常量 + 设置 schema(settings 命名空间)
│   ├── http.ts               #   HTTP / CSRF 工具
│   ├── delete-session.ts     #   会话删除流水线(detach/清理/护栏)
│   ├── request-headers.ts    #   请求头注入桥(全局 + 按提供方/按模型覆盖)
│   ├── fallbacks.ts          #   回退链聚合入口(re-export)
│   ├── fallbacks-decision.ts #   回退决策内核(配置裁剪/链解析/状态机纯函数)
│   ├── fallbacks-bridge.ts   #   回退事件桥(agent 事件 + 调用记录 + 状态快照)
│   ├── virtual-adapter.ts    #   虚拟 EnhanceChain 适配器(registerAdapter)
│   ├── call-log-store.ts     #   调用记录持久化(storageDomain 域存储)
│   └── routes.ts             #   HTTP 路由(删除/状态/回退/status/log/models/events)
├── client/                   # 客户端源码(TypeScript,tsdown 打包 → client/client.js)
│   ├── index.ts              #   入口 apply(settingsScope bind / slots / 菜单注入)
│   ├── types.ts              #   客户端结构化 context 类型
│   ├── selectors.ts          #   常量与官方 UI 选择器
│   ├── locales.ts            #   中英文案字典 + makeT(键受 Dict 接口约束)
│   ├── toast.ts              #   toast 提示
│   ├── dialog.ts             #   删除确认对话框
│   ├── shared-styles.ts      #   共享样式常量
│   ├── collapsible.ts        #   折叠卡片组件(设置分区用)
│   ├── settings-section.ts   #   「增强设置」分区组件(settings.section slot)
│   ├── session-menu.ts       #   会话菜单注入(DOM hack)
│   ├── delete-flow.ts        #   删除流程(status → confirm → delete → refresh)
│   ├── request-headers-editor.ts # 请求头编辑器(全局/提供方/模型三层)
│   ├── fallbacks-editor.ts   #   回退链编辑器(多链 + 直方图 + 运行状态)
│   ├── fallbacks-poll.ts     #   共享状态 feed(SSE 优先/轮询降级)
│   ├── fallbacks-notify.ts   #   回退切换 toast(历史差量)
│   └── styles.ts             #   注入样式
├── lib/                      # 构建产物(tsc:host 编译 JS + *.d.ts,gitignore,由 prepare/build 生成)
└── client/client.js          # 构建产物(tsdown:__ModuleLoader__ 单 bundle,提交——loader 契约,须字节稳定)
pnpm install   # prepare 会自动跑 pnpm build
pnpm typecheck # tsc 两项目类型检查(host + client)
pnpm build     # tsc 编译 host → lib/ + tsdown 打包 client → client/client.js
pnpm test      # node --test(75 个用例:manifest/删除/请求头/回退链/路由/持久化/一致性)
pnpm check     # typecheck + build + test + preflight(发布前全量门禁)

客户端受 dsh 模块系统限制(/plugins/<id>/client.js 单文件加载),源码拆分后 用 tsdown 打包成单 bundle(与官方 client 包 src→lib 构建一致,产物为 window.__ModuleLoader__.load({ id, factory }) 工厂格式,由 scripts/normalize-client-banner.mjs + scripts/preflight.mjs 保证契约)。 lib/ 不入库(gitignore):克隆后先 pnpm install(prepare 自动构建)或 pnpm build, 否则 Host 半侧(main: lib/index.js)无法加载。改动 src/**/*.ts 后运行 pnpm build 再重启 dsh。

问题记录见 FIX-PLAN.md,合规审查见 QA-REVIEW.md