deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:huhaodong/dsh-auto-driving
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
DeepSeek Harness (DSH Desktop) 插件 —— 模型自动回退 · 任务自动重试 · 全自动模式
让未完成的任务自己找路继续:一个模型挂了就换下一个,一次请求卡住了就自动救活,人工验证全部自动通过。
exit_plan_mode 方案评审)全部按默认允许自动应答,任务不再卡在人工验证;每次自动决策都追加到工作区根目录的 AUTO-MODE.md 审计文件,随时可查。<textarea> 与新版 DSH 的 Lexical contenteditable 输入框;空闲空间按尾组左缘 − 其左侧紧邻组右缘计算(输入条中间的 modes 组宽度不会被误算成空闲空间)。恢复文字带自校准迟滞区间:文字只在尾组空闲空间大于「文字本身重新占据的宽度 + 安全余量」时才回来(下限 140px,实际宽度按当前字体实时测量),因此文字回来永远不会把尾组再次顶到换行、换行也不会再次把文字挤掉——长模型名停在临界宽度时也不会在紧凑/展开之间来回闪烁、顶动页面。安装后会在 设置 界面新增一个「自动驾驶」标签页(排在「插件」之后)。子菜单栏与「刷新 / 放弃修改 / 保存」操作按钮固定在页面上方,不随内容滚动;页内通过子菜单在「回退分组 / 任务重试 / 运行状态 / 全自动模式」四个功能页之间切换,各功能页的未保存草稿在切换后保留:
| 「回退分组」子页 | 「全自动模式」子页 |
|---|---|
![]() |
![]() |
| 「任务重试」子页 | 对话中的切换可视化与通知 |
|---|---|
![]() |
![]() |
「回退分组」子页提供:
QUOTA/ACCOUNT_QUOTA 码、insufficient_quota 类文案、中英欠费措辞与 AUTH 类下的密钥无效措辞)时,该账户被自动标记并显示红色「欠费」徽标;之后的模型跳转全部跳过该 key 的模型组(含其 modlens 包装路由)。点击徽标旁的「×」手动清除后,该 key 的模型组立即回到回退池;模型级权限拒绝(如「该 key 不可用此模型」的 403)不会落标记,该账户其它模型照常参与回退。/dsh-model-fallback/api/log 同源接口读取(与宿主日志同源)。「任务重试」子页是独立的功能页,拥有独立的「放弃修改 / 保存」按钮组;保存时通过设置通道写回 retry 字段,宿主端在下一个请求立即生效。输入内容即时校验(非法数值按 0 处理,错误码自动去空)。在只读连接(非本机回环)下所有控件禁用。
| 设置项 | 控件 | 说明 |
|---|---|---|
| 启用任务重试 | 复选框 | 任务重试总开关;标题旁实时显示「已启用 / 未启用」徽标 |
| 循环级重试 | 复选框 | 流中途失败时,由 agent 循环丢弃半截消息并在同一模型上整轮重发(与次数/延迟设置共用);挂载于官方 agent/request-error 恢复点,原生供应商策略优先,原生放弃后接管 |
| 轮次保活(继续唤醒) | 复选框 | 整轮任务死于瞬态模型异常(「本轮运行失败」出现)时,自动向对话发送「继续」唤醒任务,直到成功;仅可重试类失败触发,全部分组欠费即停止(默认开启) |
| 首次唤醒延迟(ms) | 数字输入框 | 轮次失败后等待多久发送第一条「继续」;连续失败按退避倍率递增 |
| 退避倍率 | 数字输入框 | 连续失败时唤醒间隔的倍率(2 = 5s → 10s → 20s…,可调) |
| 退避上限(ms) | 数字输入框 | 「继续」间隔的上限;长时间故障下仍保持约每分钟一次探测 |
| 最大连续次数 | 数字输入框 | 同一会话连续自动「继续」的上限;0 = 不限(一直尝试直到成功或模型池全部欠费),超过后停止直到用户介入或成功回合 |
| 幂等护栏 | 复选框 | 发送「继续」前检查上一步工具调用:结果未确认或已成功时附加指引,提示模型先确认状态、不要重复执行副作用操作(默认开启) |
| 继续文本 | 文本输入框 | 中断后自动发送的文本;支持 {code} {message} {status} {tool} {turn} {errorCount} {elapsed} 占位符,留空用默认「继续」 |
| 超限时的继续文本 | 文本输入框 | 达到输出 token 上限时自动发送的文本(同样支持占位符);留空用默认「继续」 |
| 自定义可恢复错误 | 多行文本 | 每行一个普通文本片段(不区分大小写);命中错误码、HTTP 状态或消息时强制视为可恢复,优先于内置分类(参考 dsh-auto-continue 的 retryableErrorPatterns) |
| 最大重试次数 | 数字输入框 | 候选链全部失败后,用原始模型重试的最大次数(0 表示不重试) |
| 基础延迟(ms) | 数字输入框 | 首次重试前的等待时间;后续每次翻倍,单次上限 10 秒 |
| 可重试错误码 | 文本输入框 | 逗号、空格或中文逗号分隔;留空 = 除已知模型错误外全部重试 |
| 全部供应商兜底 | 复选框 | 默认关闭:选定分组全部失败后,其余已配置供应商的模型追加为最后候选(可能消耗未勾选供应商的额度)。默认行为严格限定候选池 = 勾选的分组,未勾选一律不参与 |
| 重试间隔预览 | 只读提示 | 按当前设置实时计算,如 500ms → 1s → 2s |
| 恢复默认 | 按钮 | 一键把各设置项填回默认值(需再点「保存」生效) |
多层恢复机制:
WATCHDOG_IDLE 失败,走正常的切换 / 重试管线重新激活任务。报错之外,无报错的"假死"同样处理:连接正常且未欠费的请求(402/403 会立即报错,不会静默)长时间无任何输出时照样触发。TRANSPORT(供应商流连接失败)、NETWORK_ERROR、TIMEOUT、CONNECTION_CLOSED 四类瞬态错误始终可重试,不受自定义码表限制。PI_AI_ERROR 等 catch-all 码按 message 里的真实状态分类——429/5xx/超时/断连 → 瞬态可重试;401/402/403 → 持续型靠切换。中文网关文案("模型服务暂时不可用,请稍后重试"等)同样按瞬态识别;中文欠费措辞("余额不足"等)与官方 isQuotaExceededError 词汇("Insufficient Balance"、"insufficient_quota"、"quota exceeded" 等)按账户级失败识别并自动落欠费标记,AUTH 类下的密钥无效措辞("invalid api key"、"鉴权失败"等)同样落标记(对齐徽标「欠费或鉴权失败」口径)。默认可重试码表也已对齐 pi-ai 适配器词汇(SERVER/RATE_LIMIT/PI_AI_ERROR),链耗尽后的同模型重试在复查时同样携带 message 重新分类(修复此前"retry 1 surfaced non-retryable (SERVER); giving up"的过早放弃)。llm/retry 事件(与 DSH 自带的「已重试模型请求」同一渲染通道),对话消息流里出现一条可展开的重试行,写明「A → switching to B」;同一请求内的所有切换共享同一 retryId,UI 把它们合并成一条不断增长的切换链。切换是真实的调用线路替换:每个接管候选都经 ctx.llm.adapterStream({ provider, model }) 直连新供应商/模型派发,日志与对话行里的每个模型报出各自的真实错误(402/上下文超限等)。链耗尽后的同模型重试(带指数退避)也追加到同一条链(带 delayMs 倒计时),切换与重试构成一条完整的恢复时间线。这些事件经 SSE 长连接(/dsh-model-fallback/api/events)实时推送到客户端,输入框上方气泡毫秒级弹出(单实例、保留 30 秒,且只弹在发起请求的那个对话页面上),输入区的对话框模型显示同步为接管模型(同步同样只响应该会话自己的切换事件)。PI_AI_ERROR 包裹 "context length exceeded"),也会先按 message 里的确定性措辞拦截,绝不盲发「继续」让任务在同一超限请求上死循环。停止条件即用户口径:选定分组全部欠费(或未选任何分组)时不再发送,让失败如实呈现;否则连续失败按指数退避(默认 5s 起、倍率 2、封顶 60s,均可调;maxConsecutive 可设上限,0 = 不限),任一轮正常结束(任务推进/成功)即复位退避计数。唤醒文本是模板:continueText 支持 {code} {message} {status} {tool} {turn} {errorCount} {elapsed} 占位符(如 继续 ({tool}: {code}) → 继续 (git push: UPSTREAM));达到输出 token 上限(turn/end max-tokens)时用另一套模板 continueTextMaxTokens 唤醒(如「继续输出,不要重复已生成的内容」)。幂等护栏(guardTools,默认开)在发送前检查上一步工具调用:结果未确认(回合在工具执行中途夭折,如 git push 可能已经推上去了)时,续跑消息会提示模型先确认状态、不要重复执行;工具已确认成功时说明已完成、请勿重复;工具失败则不加护栏(重试本来就是目的)。自定义可恢复错误(retryablePatterns,每行一个普通文本片段)可让 provider 专属但确认安全的失败强制走自动续跑,优先于内置分类。agent 上存在已暂停/阻塞的目标时优先恢复目标而不是注入消息,避免与目标机制打架;agent 正在运行或收件箱已有待处理消息(用户自己在操作)时跳过本次注入,等下一次失败再武装。「全自动模式」子页(默认关闭,需显式开启):
| 设置项 | 控件 | 说明 |
|---|---|---|
| 启用全自动模式 | 复选框 | 总开关,默认关闭;标题旁实时显示「已启用 / 未启用」徽标。这是全局总闸兼默认值——关闭后输入条胶囊上的「自动驾驶」半边从所有对话消失,任何对话都无法单独启用;开启时每个对话可在胶囊上单独开关(会话隔离,互不影响) |
| 自动允许权限审批 | 复选框 | 工具执行所需的权限审批(命令沙箱提权、文件写入确认等)一律按允许处理 |
| 自动应答确认提问 | 复选框 | ask_user_question 等人工选择按推荐项(第一个选项)自动应答 |
| 自动批准方案评审 | 复选框 | exit_plan_mode 提交的实施方案按「批准」自动通过 |
| 工作区审计日志 | 复选框 | 把每一次自动决策追加到会话工作区根目录的 AUTO-MODE.md |
全自动模式开启后,插件在会话工作区根目录创建/追加 AUTO-MODE.md:
# ⚡ 全自动模式操作审计(dsh-auto-driving)
> 本文件由插件「全自动模式」自动写入:所有被自动允许的权限审批与自动应答的人工确认都会记录在此。
> 关闭方法:设置 → 全自动模式 → 关闭「启用全自动模式」。
| 时间 | 类型 | 内容 | 结果 |
| --- | --- | --- | --- |
| 2026-08-31T12:30:00.000Z | 权限审批 | 工具 bash — sandbox escalation to danger-full-access | 已自动允许 |
| 2026-08-31T12:31:12.000Z | 方案审批 | Approve this plan and leave plan mode? | 已自动允许 |
审计写入为 fire-and-forget:日志失败绝不阻塞它所记录的审批流程。
一行安装(github: 源,适用于任何 DSH profile,desktop / web 均可):
dsh plugin --profile desktop add github:huhaodong/dsh-auto-driving
或在 dsh-market 插件市场(设置 → Plugin Market)里搜索 dsh-auto-driving 一键安装——本插件按 awesome-dsh-plugin 收录规范发布,收录合并后市场会自动同步(通常一天内)。
也可以从本地目录安装(desktop 是 DSH Desktop 默认 profile 名):
dsh plugin --profile desktop add /path/to/dsh-auto-driving
或从任意其它目录用相对路径(会被锚定到调用目录):
cd /path/to/dsh-auto-driving && dsh plugin --profile desktop add .
安装完成后重启 DSH Desktop(宿主端插件与客户端设置页都在启动时装载)。
[!NOTE] 本仓库 / 项目名为
dsh-auto-driving;插件包名(package id)目前仍为dsh-model-fallback,dsh plugin remove等命令请继续使用该 id。
卸载:
dsh plugin --profile desktop remove dsh-model-fallback
@deepseek-ai/dsh-settings 世代——0.0.1-rc.x ~ 0.1.6 的 section registry 旧缝与 0.1.7 ~ 0.2.x 的 SettingsForms 新缝(DSH NEXT),旧版桌面宿主(2.0.x)与网页端同一套代码。test/compat.mjs 跨版本矩阵用 4 个真实发布世代 × 2 种宿主设置缝逐一加载插件并完成设置装配验证。settings.register 时走旧注册表,暴露 settings.describe 时走新表单镜像;dsh-settings 0.1.7 起删除了 installSettingsSection / settingsNamespace 两个工具函数,插件内置等价实现自动兜底——无论宿主解析到哪一代设置服务,模块都能加载、设置页都能装配。@deepseek-ai/* 一律声明为 peerDependencies(并列 devDependencies,与 harness 包自身做法一致),插件使用宿主安装的那一份设置服务,不与宿主抢版本、不产生「多版本核心包」;peer 范围用逐元组预发布分支(^0.1.7-0 || …)显式放行每一个 rc / alpha 构建——普通范围写法(如 >=0.0.1 <0.3.0)在 semver 规则下会静默排除全部预发布版本,正是插件市场最常见的安装失败原因。platform: web 与官方 @deepseek-ai/dsh-client-* 包边;输入条定位兼容 <textarea> 与新版 Lexical contenteditable;设置通道在 settingsScope(旧宿主)与 remote.settings(NEXT)之间自动择一,两者都不可用时显式报错而不是静默失效;只读连接(非本机回环)下所有写操作控件自动禁用。allowBuilds 构建授权),路径 / 文件 / 时间全部走 node:* 标准 API,macOS / Windows / Linux 行为一致。slots / locale / connection / remote),可变的 wire face(remote.llm / remote.session / remote.settings)一律使用时经 ctx.get 可选查询防御解析——上下文代理对未声明的 ctx.remote.<face> 属性访问会直接抛 cannot get property … without inject,而声明但被宿主改名的服务会令插件永久 pending(settingsScope 已发生过),ctx.get 正是官方"免 inject 声明"的可选查询口;设置传输缺失时设置页降级为只读占位、传输晚到自动自愈为真实镜像;userQuestions 等服务晚注册或被宿主重建时自动重挂包装(ctx.inject 逐实例武装);配置形状畸形同样只降级不崩——providers 等字段从补丁层 / 设置写入拿到任何形状(字典、数字、布尔)都按「无候选」处理并告警,绝不让激活崩溃带走全部开关(真实案例:profile 补丁里 providers 被写成字典 → providers is not iterable → 插件激活即死、两个开关全部失效,0.7.2 修复);locale / logger / session.append 等宿主 API 改形都有守卫;SSE 长连接不可用降级间隔轮询;任一单项能力缺席只会让对应功能降级,不会带走整个插件。StreamChunk 的控制帧(block-start / usage / finish)不计为可见内容,其余未知帧一律按已产出内容处理——未来宿主新增内容型帧时绝不会在已显示的输出上二次流式(消息重复/损坏),最坏情况只是切换窗口变小。npm test 五组测试——smoke(回退/重试/看门狗/欠费跳转 + 未知帧防护 / 无 append 会话 / 晚注册服务重挂)、e2e(真实场景链路)、client UI(客户端交互、NEXT 设置镜像、无设置传输降级与自愈)、next-settings(DSH NEXT 设置缝回归)、compat(跨世代加载矩阵)。llm/stream waterfall 上注册监听器:凡是 provider 命中已选分组的请求都会被包一层回退循环。LlmRuntime.adapterStream)换下一个候选模型重新派发——上层 invariant 校验的是原始请求头,因此不会被中途换模型破坏。llm.listModels 缓存 5 分钟,并在设置变更 / 适配器拓扑变化时自动刷新。protectUnselected,默认开启):请求模型打头、选定分组作为候选池,任何请求都有恢复网;关闭后未选定分组原样放行。approval/request waterfall 上注册监听器,启用时直接返回 allowed-once 并写入审计,停用时 next() 交还原有应答链(fail-closed 语义不变);包装 userQuestions.registerProvider 的 UI provider——带意图(intent)的问题按 intent.approve 标签应答,无意图的问题按第一个选项(推荐项)应答,自由文本按默认应答,任何子开关关闭时完整委托给真实 provider;同时向 system prompt 注入说明,让模型知道无需等待人工、直接继续任务。model-fallback: … failed (CODE: …); switching to provider/model),成功恢复时记录 request recovered on provider/model;每条路由每 5 分钟记录一条参与决策日志(model-fallback: engaged for <provider>/<model>, chain=N candidate(s) / not engaged: ...),随时可在宿主日志确认插件是否真实介入。配置保存在 settings 命名空间 model-fallback(随 settings.yaml 持久化),字段:
| 字段 | 类型 | 说明 |
|---|---|---|
enabled |
boolean(默认 true) |
总开关(全局总闸兼默认值:关闭后胶囊的「模型回退」半边从所有对话消失,sessionModes 里的旧钉住状态也不再生效;开启时每个对话可在输入条胶囊上单独开关,见 sessionModes) |
providers |
string[](默认 []) |
参与循环的供应商路由,按优先级排序 |
protectUnselected |
boolean(默认 true) |
未选定分组的请求也纳入保护(请求模型打头,选定分组作候选池) |
allProvidersFallback |
boolean(默认 false) |
选定分组全部失败后,其余已配置供应商的模型追加为最后候选 |
arrears |
Record<string, boolean>(默认 {}) |
欠费账户标记:key 为账户名(modlens- 前缀剥离后的 API key 名)。宿主检测到钱包级 / 凭据级失败(402、额度耗尽、密钥无效)时自动写入 true 并把该账户全部模型移出回退池,每次标记都会写日志(wallet/credential-level failure on …);在设置界面点击「×」清除后立即恢复 |
providerModels |
Record<string, string[]>(默认 {}) |
每个供应商分组勾选的模型池:value 为可作回退候选的模型 id 列表;缺省 key = 全部模型可用,空数组 = 该分组不贡献候选 |
sessionModes |
Record<string, { auto: boolean\|null, fallback: boolean\|null }>(默认 {}) |
按对话隔离的开关(会话隔离开关):key 为会话 id,fallback / auto 为该对话单独钉住的模型回退 / 全自动状态;null 或缺省 = 跟随全局默认。钉住状态仅在对应模式的全局总闸开启时生效(总闸关闭即不可用,旧钉住不会复活被全局关闭的模式)。由输入条胶囊通过 POST /dsh-model-fallback/api/session-state 写入;子代理会话沿 parentSession 链继承所属对话的钉住状态 |
watchdog.enabled |
boolean(默认 true) |
活性看门狗开关 |
watchdog.idleTimeoutMs |
number(默认 300000) |
静默判定阈值(ms),最低 250 |
watchdog.resends |
number(默认 2) |
判定卡住后同一模型原样重发的次数 |
retry.enabled |
boolean(默认 true) |
任务重试总开关 |
retry.maxRetries |
number(默认 3) |
候选链耗尽后的最大重试次数 |
retry.baseDelayMs |
number(默认 500) |
首次重试基础延迟(ms),后续每次翻倍(上限 10s) |
retry.retryableCodes |
string[](默认 ["NETWORK_ERROR","TIMEOUT","CONNECTION_CLOSED","RATE_LIMITED","SERVER_ERROR","500","502","503","504"]) |
触发重试的错误码列表;留空时除已知模型错误外的所有错误均可重试 |
retry.loopRetry |
boolean(默认 true) |
循环级整轮重发(流中途失败恢复) |
retry.keepAlive.enabled |
boolean(默认 true) |
轮次保活总开关 |
retry.keepAlive.delayMs |
number(默认 5000) |
首次唤醒延迟(ms) |
retry.keepAlive.maxDelayMs |
number(默认 60000) |
唤醒间隔上限(ms) |
retry.keepAlive.backoffFactor |
number(默认 2) |
连续失败的退避倍率 |
retry.keepAlive.maxConsecutive |
number(默认 0) |
连续自动「继续」上限(0 = 不限) |
retry.keepAlive.guardTools |
boolean(默认 true) |
幂等护栏:续跑前检查上一步工具调用 |
retry.keepAlive.continueText |
string(默认 "") |
唤醒文本模板(支持 {code}/{message}/{status}/{tool}/{turn}/{errorCount}/{elapsed};留空 = 默认「继续」) |
retry.keepAlive.continueTextMaxTokens |
string(默认 "") |
达到输出 token 上限时的唤醒文本(留空 = 默认「继续」) |
retry.keepAlive.retryablePatterns |
string(默认 "") |
自定义可恢复错误:每行一个普通文本片段,命中即强制视为可恢复 |
全自动模式保存在独立命名空间 model-fallback-auto(同样随 settings.yaml 持久化):
| 字段 | 类型 | 说明 |
|---|---|---|
enabled |
boolean(默认 false) |
全自动模式总开关(默认关闭;全局总闸兼默认值——关闭后胶囊的「自动驾驶」半边从所有对话消失,任何对话都无法单独启用;开启时每个对话可在输入条胶囊上单独开关) |
autoAllowPermissions |
boolean(默认 true) |
自动允许权限审批 |
autoAnswerQuestions |
boolean(默认 true) |
自动应答确认提问 |
autoApprovePlans |
boolean(默认 true) |
自动批准方案评审 |
workspaceLog |
boolean(默认 true) |
写工作区审计日志 |
也可直接编辑 settings.yaml:
model-fallback:
enabled: true
providers:
- deepseek
- your-gateway
# 欠费账户标记(宿主自动写入,可在设置界面手动清除)
arrears:
your-gateway: true
# 每个分组的模型池勾选(缺省 = 全部模型可用)
providerModels:
deepseek:
- deepseek-chat
- deepseek-reasoner
watchdog:
enabled: true
idleTimeoutMs: 300000
resends: 2
retry:
enabled: true
maxRetries: 3
baseDelayMs: 500
retryableCodes:
- NETWORK_ERROR
- TIMEOUT
- CONNECTION_CLOSED
- RATE_LIMITED
- SERVER_ERROR
- "500"
- "502"
- "503"
- "504"
keepAlive:
enabled: true
delayMs: 5000
maxDelayMs: 60000
backoffFactor: 2
maxConsecutive: 0 # 0 = 不限
guardTools: true
continueText: "继续" # 支持 {code}/{message}/{status}/{tool}/{turn}/{errorCount}/{elapsed}
continueTextMaxTokens: "继续输出,不要重复已生成的内容"
retryablePatterns: |-
# 每行一个普通文本片段,命中即强制视为可恢复(可留空)
# Upstream rejected the request as invalid
MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。