dsh-all-enhance
DSH(DeepSeek Harness)功能增强插件
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 28, 2026
- Updated
- Sep 1, 2026
Introduction
dsh-all-enhance
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-settings 的 installSettingsSection()(生命周期托管) |
| 运行中会话检测 | 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'], …) —— 每条链一行 listModels,stream() 薄委托到链 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 头注入)。
兼容性与已知限制
- 需要服务:
webServer、settings、workspaceRegistry、sessionPersistence、sessions。 - Web 客户端目前对设置页导航与会话列表菜单没有官方扩展 slot;客户端使用内部哈希类名(
.VOzbGW_*、.YDXeBa_*)与 React fiber 探测做 DOM 注入,DSH UI 升级可能需要维护选择器。可迁移的官方 slot:settings.plugin.item、conversation.chat.node。 - 归档集无公开 unarchive API(
archivedSessionIds只增不减):删除已归档会话后归档集保留失效 id。 - 进程内 header 索引缓存无公开清理 API:删除后由重启 bootstrap 自愈。
locate()只返回主日志产物;compaction snapshot 等后端附加产物不在公开接口内,不清理。- 模型回退链依赖当前 DSH 的
agent/request-error/agent/request事件契约与ctx.llm.registerAdapter;宿主升级若调整失败码或适配器注册机制可能需要插件跟进(未知失败码不会触发回退,安全降级)。 - 流中断回退覆盖适配器层失败(连接断开、空闲超时 —— 归一为
TRANSPORT/TIMEOUTfinish 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。