Bundle
dsh-all-enhance
DSH 功能增强插件:会话删除、增强设置等(基于官方公开服务接口)
- Source
- rangdl
- License
- MIT
- Updated
- Updated yesterday
Readme
# dsh-all-enhance
[](https://www.npmjs.com/package/dsh-all-enhance)
[](https://www.npmjs.com/package/dsh-all-enhance)
[](LICENSE)

[](https://dshfind.com/zh/plugins/rangdl/dsh-all-enhance?ref=badge)
DSH(DeepSeek Harness)功能增强插件:会话删除、模型请求头、模型回退链 —— 三大功能均可在 Web 设置页的「增强设置」分区中开关与配置。
> 中文 | [English](./README.en.md)
## 快速安装
需要 dsh(DeepSeek Harness),一条命令安装到目标 profile(如 `web`),重启后生效:
```sh
dsh plugin --profile web add dsh-all-enhance
```
推荐固定稳定版本,避免后续发布悄悄改变实际运行的代码:
```sh
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 层的组合包:
```sh
dsh plugin --profile <name> add dsh-all-enhance
```
或从源码检出(使用 `pnpm dsh`):
```sh
pnpm dsh plugin --profile <name> add ./dsh-all-enhance
```
组合包注册一条 Cordis 配置项(`id: all-enhance`),同时挂载 Host 半侧与 Web 客户端半侧。
### 本机免 pnpm 安装(pnpm 受限环境)
当 `dsh plugin add`(内部走 pnpm)不可用时,可直接手工安装(无需删除任何东西):
```sh
# 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`/`TIMEOUT` finish chunk)。`llm/stream` 管道内消费者/中间件层失败会完全绕过 `agent/request-error`,在宿主循环中即为终态 —— 任何插件都无法恢复。
- 切换 toast 优先走 SSE 推送;流不可用时客户端降级为约 5 秒的共享轮询,通知最多延迟一个轮询周期。
## 参考项目
- [btspoony/dsh-llm-fallbacks](https://github.com/btspoony/dsh-llm-fallbacks)(MIT)— 回退链的决策内核参考:`agent/request-error` 接管恢复、冷却抑制与 half-open 恢复的机制均源于此(本插件剥离了其角色/时段/subagent 策略,并对齐 dsh 0.1.1-rc.2 的失败码与事件契约;其 issue #52 的会话事件坑位也在本插件规避)。
- [dsh-custom-provider-settings](https://github.com/topics/dsh-plugin)(社区插件)— 请求头注入链路参考:`llm/stream` 事件 + `AsyncLocalStorage` + fetch 包装的同构实现(本插件 TS 化并改为自有设置命名空间,runtime key 亦区分,两者可共存)。
- [dsh-market](https://github.com/dsh-market/dsh-market) — 构建方式镜像:tsc 编译 host + tsdown 打包 client、结构化服务子集类型与 bundle/patch 层组织方式。
- [deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) — 宿主框架(DSH)官方仓库。
## 开发
源码为 TypeScript,构建方式镜像 [dsh-market](https://github.com/dsh-market/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 契约,须字节稳定)
```
```sh
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`。
Install
dsh plugin --profile web add github:rangdl/dsh-all-enhance
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-all-enhance from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.