DeepSeek Harness 个人工具箱:可运行时开关的体验优化,每项在工具箱设置页里就是一张卡片。
30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/14 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/kolawong/dsh-plugin-toolkit.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
生态插件
可安装,未声明 dsh 能力
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 22 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意未发布到 npm registry,仅可从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-plugin-toolkit(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=18 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/12 17:36:47
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 29 处,展示前 26 条)
- 写入本地文件index.js:268
await mkdir(path, { recursive: true }); - 写入剪贴板client.js:79
clipboard.writeText(text).then( - 发起网络请求client.js:1343
const res = await fetch("/api/toolkit/slash-translations"); - 发起网络请求client.js:2208
const response = await fetch(smartReplyPath(), { - 发起网络请求client.js:3182
const res = await fetch("/api/toolkit/translate-slash", { - 发起网络请求client.js:3236
const res = await fetch("/api/toolkit/translate-slash", { - 发起网络请求client.js:3281
const res = await fetch("/api/toolkit/slash-translations", { - 发起网络请求client.js:5421
const response = await fetch(path); - 发起网络请求model-sync.js:262
const response = await fetch(url, { - 读取本地文件slash-i18n-api.js:36
if (existsSync(credPath)) { - 读取本地文件slash-i18n-api.js:37
const content = readFileSync(credPath, "utf8"); - 读取本地文件slash-i18n-api.js:71
if (existsSync(dshSkillsDir)) { - 读取本地文件slash-i18n-api.js:73
const entries = readdirSync(dshSkillsDir); - 读取本地文件slash-i18n-api.js:82
if (existsSync(file)) { - 读取本地文件slash-i18n-api.js:83
const content = readFileSync(file, "utf8"); - 写入本地文件slash-i18n-api.js:131
writeFileSync(file, JSON.stringify(payload, null, 2), "utf8"); - 请求外部主机api.deepseek.comslash-i18n-api.js:227
const resp = await fetch("https://api.deepseek.com/chat/completions", { - 发起网络请求slash-i18n-api.js:227
const resp = await fetch("https://api.deepseek.com/chat/completions", { - 请求外部主机ark.cn-beijing.volces.comslash-i18n-api.js:265
const resp = await fetch("https://ark.cn-beijing.volces.com/api/coding/v3/chat/completions", { - 发起网络请求slash-i18n-api.js:265
const resp = await fetch("https://ark.cn-beijing.volces.com/api/coding/v3/chat/completions", { - 读取本地文件suggest-store.js:68
if (typeof path !== "string" || path === "" || !existsSync(path)) return createHabitState(); - 读取本地文件suggest-store.js:69
const raw = readFileSync(path); - 写入本地文件suggest-store.js:91
mkdirSync(dirname(path), { recursive: true }); - 写入本地文件suggest-store.js:93
writeFileSync(temp, json, { encoding: "utf8", mode: 0o600 }); - 写入本地文件suggest-store.js:94
renameSync(temp, path); - 删除本地文件suggest-store.js:102
unlinkSync(temp);
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
涉及外部主机:api.deepseek.com、ark.cn-beijing.volces.com
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 运行时开关体验优化
- 设置页卡片式管理
README
由 DeepSeek 最新模型翻译生成English · 简体中文
DeepSeek Harness 的个人体验优化工具箱。每项优化都太小,不值得单独做成一个垂直插件,所以它们打包在一起,作为 Toolkit 设置卡片里的一项:Web settings → Plugins → DSH-Toolkit 会展开半宽的子卡片网格(图标、标题、简短副标题、开关徽标),点击子卡片即可打开对应优化的对话框。插件列表再长,插件页也始终是个紧凑的启动器。
一切都通过公开扩展接口接入 —— 设置命名空间、客户端模块表、会话槽位、llm/stream、模型发现和 webServer 路由 —— 所以不用给任何 dsh 核心包打补丁,上游一键升级也不会受影响。
展开完整文档(README 剩余 1 节)
English · 简体中文
DeepSeek Harness 的个人体验优化工具箱。每项优化都太小,不值得单独做成一个垂直插件,所以它们打包在一起,作为 Toolkit 设置卡片里的一项:Web settings → Plugins → DSH-Toolkit 会展开半宽的子卡片网格(图标、标题、简短副标题、开关徽标),点击子卡片即可打开对应优化的对话框。插件列表再长,插件页也始终是个紧凑的启动器。
一切都通过公开扩展接口接入 —— 设置命名空间、客户端模块表、会话槽位、llm/stream、模型发现和 webServer 路由 —— 所以不用给任何 dsh 核心包打补丁,上游一键升级也不会受影响。
目录
- 一览
- 安装
- 架构
- 设置卡片
- 优化项
- workspacelessChat
- editLastMessage
- viewActivity
- slashI18n
- changeReport
- opencodeSession
- modelCapability
- smartReply
- 配置参考
- 开发
- 毕业规则
- 模型体验
- 许可证
一览
| # | 优化项 | 作用 | 默认 | 半区 |
| 1 | workspacelessChat | 保留一个无项目聊天工作区,冷启动时自动连接 | 开 | 客户端 + 宿主 |
| 2 | editLastMessage | 编辑并重发最后一条用户消息,回退模型上下文 | 开 | 客户端(宿主 session.rewrite RPC) |
| 3 | viewActivity | 侧边栏活动图标:运行中优先,然后按今天 / 昨天 / 本周 / 更早 | 开 | 客户端 |
| 4 | slashI18n | 为 / 菜单的命令和技能提供中文描述 | 开 | 客户端 |
| 5 | changeReport | 回合末尾显示 Codex 风格的文件变更卡片 | 开 | 客户端 |
| 6 | opencodeSession | 在 OpenCode Go/Zen 请求上稳定发送 x-opencode-session 头 | 开 | 宿主 |
| 7 | modelCapability | 保持某条 llm-pi-ai 路由的模型列表、容量和图像支持为最新 | 开 | 宿主 + 客户端 |
| 8 | smartReply | 在输入框里显示灰色幽灵文本建议回复,按 Tab 填入 | 开 | 客户端 + 宿主 |
安装(web 配置档)
cd ~/.dsh/profiles/web
pnpm add file:/path/to/dsh-plugin-toolkit # link:/path/to/dsh-plugin-toolkit while developing
把 dsh-plugin-toolkit 加到 package.json 里的 dsh.profile.bundles 列表 bash
systemctl restart deepseek-harness.service # 或者你配置档对应的重启方式
服务端改动(也就是本包)需要重启配置档;客户端 bundle 要在配置档里跑 pnpm install 重新同步,然后浏览器强制刷新。开关、路径和模型列表都是 settings 命名空间里的值,日常修改即时生效,不用重启。
架构
本包分成浏览器一半和宿主一半;两边读同一个 toolkit settings 命名空间,也都不碰 dsh 源码。
| 一半 | 文件 | 职责 | 用到的接缝 |
| 客户端 | client.js | 设置卡片、行内编辑 UI、侧边栏重排、/ 菜单改写、变更报告卡片、输入框幽灵文本建议、模型 id 选择器 | 客户端模块表、会话节点/轮次尾部插槽、conversation.input.overlay、inputActions、remote. 命名空间包装、settings 作用域 |
| 宿主 | index.js | settings schema 与默认值、默认聊天工作区目录、OpenCode 会话头打标 | settings、llm/stream、globalThis.fetch |
| 宿主 | model-sync.js + models-core.js | 模型发现包装、同步/列表路由、容量与模态信息补全,以及 suggest-api.js 复用的端点/凭据解析器 | llm 模型发现、webServer 路由、settings 存储 |
| 宿主 | suggest-api.js + suggest-core.js | 建议路由,以及它背后纯逻辑的提示词/抢救处理 | webServer 路由、model-sync 解析器 |
全包通用的设计规则:
- 不打核心补丁。 每个钩子都是官方文档写明的扩展面,所以上游升级不会和 toolkit 冲突。
- 休眠,而不是坏掉。 缺某个接缝(旧宿主、没有 webServer、没有 session.rewrite)只会关掉那一项优化;UI 其余部分不受影响。
- 配置即时生效。 每个开关和字段都放在 toolkit settings 命名空间里,用的时候才读。
- 开关互相独立。 每项优化都能在自己的子卡片里关掉,关掉后原样恢复默认行为。
设置卡片
卡片为每项优化渲染一张子卡片,带实时开/关徽标,还有一对 Enable all / Disable all。打开子卡片会显示该项优化自己的说明和字段(比如聊天目录,或者模型能力键和选择器)。
优化项
workspacelessChat
不先选工作区就能聊天,就像其他智能体框架的默认项目行为那样。dsh 的 composer 要求空白会话必须属于某个工作区,所以这项优化保留了一个专用的无项目聊天工作区(「通用对话」):只要启用优化,它就会以友好标题幂等创建,因此这个选项始终出现在侧边栏和工作区选择器里——打开它就能不选项目直接聊天。
除此之外,当两个基线都就绪、没有选中任何会话,且运行时自身的启动策略没有可连接的最新工作区时(首次运行、没有任何工作区),客户端还会在该工作区里自动连接一个空白会话,让 composer 立刻可用。
冷启动自动连接失败时最多重试 3 次(间隔 2 s),之后进入休眠,直到列表变化重新触发检查。运行时自身的最新工作区自动连接始终优先;自动连接只填补无连接可用的情形,而工作区本身无条件存在。只有当工作区仍带着自动生成的 basename 时,才会把它重命名为友好标题(你自己设置的标题永远不会被覆盖)。
| 配置 | 默认值 | 含义 |
| optimizations.workspacelessChat | true | 开关无项目聊天工作区 + 自动连接。 |
| chatWorkspacePath | "" | 默认聊天工作区的主机目录;留空则解析为 <DSH_HOME>/chat。服务器会创建该目录(设置改动时也会实时创建)。 |
| chatWorkspaceTitle | 通用对话 | 无项目聊天工作区的显示标题。 |
卡片的写入走 toolkit 设置命名空间,所以开关和路径改动实时生效,无需重启。
editLastMessage
重试失败的回复,不用复制粘贴,也不污染模型上下文:最后一条用户消息的悬停操作里多了一个 Edit 按钮。点击它会打开内联编辑器,并预填该消息;Save & resend 会就地重写会话——被编辑的消息及其之后的所有内容都被替换(模型上下文真正回退,所以下一次请求只包含编辑后的内容),然后由新一轮回复这条编辑后的消息。
这需要宿主支持 session.rewrite RPC(对本仓库 dsh checkout 的小改动)。在没有该支持的宿主上,按钮仍会显示,但会提示不支持重写。客户端记录会抹掉旧消息及其失败的回合;回合边界保留,因此失败的回合会折叠成一个不可见的空回合。需要会话处于空闲状态(正在运行的回合会以 agent-busy 拒绝);只有最后一条人类用户消息可编辑,且仅限文本。
宿主扩展以 host/session-rewrite.patch 的形式放在本仓库中。dsh 升级会丢弃它,所以每次升级后都要重新应用:node scripts/apply-host-patch.mjs,然后 pnpm run build:lib && pnpm run build:web 并重启 harness。用 node scripts/e2e-verify-rewrite.mjs 验证实际行为。
| 配置 | 默认值 | 含义 |
| optimizations.editLastMessage | true | 在最后一条用户消息上显示编辑并重发按钮。 |
viewActivity
在工作区侧边栏添加一个 Activity 图标(原生时钟字形),紧跟在搜索(放大镜)控件之后。点击它会就地重新排序侧边栏的会话列表:正在运行的会话浮到最前的 Priority 分组,其余历史按 Today / Yesterday / Weekday / Earlier 分组(每组内最新的在前)——就是熟悉的「最近会话」模式。再次点击恢复之前的分组(工作区分节或扁平列表);活动排序开启时图标会高亮。
100% 非侵入式外部实现:把活动开关挂进侧边栏头部,动态替换会话树,对官方 DSH 核心包零改动,与上游一键升级零冲突。
| 配置 | 默认值 | 含义 |
| optimizations.viewActivity | true | 工作区头部活动图标 + 就地按运行优先 / 按天重新排序。 |
slashI18n
/ 菜单的外壳文案(分组标题、仅用户徽标、骨架行)已经本地化,但条目的描述直接来自宿主:内置命令描述、参数提示、技能目录描述都原样显示英文,即使在中文界面下也是如此。这项优化在客户端翻译它们:工具包包装 remote.commands.list 和 remote.skills.list 命名空间方法,在任何消费者读取之前,通过精确匹配的英→中词典改写每个响应的 description / input.hint。
范围与安全护栏:
- name 字段永不翻译——模糊匹配、草稿词条库、声明裁定都要读它;whenToUse / modelInvocable 同样原样保留。
- 词典查不到就回退到原字符串,所以 dsh 更新改了描述措辞后,会先退化成英文,等词典跟上(菜单绝不会因此坏掉)。
- 翻译只在 UI 语言为中文时生效,设置开关按每次 RPC 结果重新读取,所以切换后下一次打开菜单就生效。
- 结果会重建为新对象,所以没有调用方缓存会别名到线上数据;拒绝和错误结果原样透传;重复应用插件不会把方法包两层。
- 你自己写的技能,直接在 SKILL.md frontmatter 里写中文 description 就行——不需要词典。词典只覆盖 dsh 自带的内容(6 个内置命令、2 条提示,这两条未被 ui-conversation 自己的 hint. locale 键覆盖,以及 2 个内置技能)。
- 不需要改 dsh 源码,也不需要宿主扩展;在没有这些命名空间的宿主上,这项优化就静静待着不生效。
| 配置 | 默认值 | 含义 |
| optimizations.slashI18n | true | 为 / 菜单的命令和技能提供中文描述。 |
changeReport
每轮完成后,在尾部生成 codex 风格的变更报告。当一轮改了文件(edit / write / 会改文件的 str_replace_editor 调用),尾部会渲染一张紧凑卡片:「N files edited · +A -R」,每个文件一行,各带自己的 +N -M(点某一行可在查看器中打开该文件),超过四行有 show more 展开器,还有一个 Review 按钮,打开这一轮的完整 diff。这张卡片是 dsh 内置 produced-files 尾部的超集:它接在同一条 conversation.chat.turnTail 链上,排在其前面(优先级更低),遇到不改文件的一轮就不接管,所以原生尾部渲染得和以前一模一样。
工作原理和边界:
- 数据来自转录,在客户端处理:会话轮次数据累加器为每次成功的改动调用记录一对 before/after hunk——包括智能体直接发起的工具调用,以及 run_code 里嵌套的子调用(它们以 tool/code-dispatch 事件记录,按根调用归键)。行数复用原语里的 diffTotals,所以头部数字始终和 review 里 DiffBlock 渲染的一致。
- bash 侧的文件写入(sed、重定向等)对转录不可见,因此不被跟踪——报告只覆盖专门的文件改动工具,就像 codex 只跟踪自己的那套。
- 失败的调用(工具错误结果)不贡献任何内容;收尾 assistant seq 之后的结算会被排除;聚合结果每次渲染都重建(不与线上数据别名)。
- 没有撤销:要在磁盘上还原文件,需要宿主机侧的能力,而浏览器没有。拒绝是刻意为之——V1 只做展示。
- 不改 dsh 源码:插槽、回合数据定义注册表、diff 原语都是公开的扩展面。关掉开关,原样的尾部就回来了。
| 配置 | 默认值 | 含义 |
| optimizations.changeReport | true | 在回合尾部显示每回合变更报告卡片。 |
opencodeSession
OpenCode Go 现在会对缺少 x-opencode-session 的请求返回 400({"type":"MissingSessionID", ...}),它的规范也要求编码智能体表明自己的身份,用自己的 user agent,并且每段对话发送一个稳定的 session id,好让网关路由请求、复用提示词缓存。user agent 这部分已经满足——每个 dsh 服务商请求都带着 deepseek-harness/<version> 归属标识。这项优化补上 session 这一半,完全在插件侧完成:一个 llm/stream 监听器通过 AsyncLocalStorage 把在途请求的会话身份沿异步链传下去,启动时再用一层薄薄的 globalThis.fetch 包装,给目标主机是 opencode.ai 或其子域名的请求(Go 和 Zen 都一样)打上这个请求头。
它的工作方式和边界:
- 这个 id 就是 dsh 已经附在每个自建请求上的、由循环打戳的会话身份(智能体循环的不变量要求如此),所以按构造,这个请求头每段对话都稳定——新对话,新 id;同一段对话,跨回合、重试、压缩、标题生成都是同一个 id。
- 不带会话身份的调用(少见的手工一次性调用;还有模型发现)会退回到每进程一个 id,而不是以网关的 MissingSessionID 失败。
- 包装只装一次(有标记守卫),只在请求头缺失时添加,任何装饰失败都落回普通 fetch——绝不会弄坏请求。非 OpenCode 请求原样字节通过。
- 服务商 SDK 每次请求都解析全局 fetch(pi-ai 每次流式调用都新建客户端),所以启动时的包装不需要改 dsh 源码,一键升级上游也照样不受影响。
- 100% 非侵入式的外部实现,和 viewActivity 一样:对 dsh 核心包零修改。
| 配置 | 默认值 | 含义 |
| optimizations.opencodeSession | true | 在 OpenCode Go/Zen 请求上带稳定的每对话 x-opencode-session 请求头。 |
modelCapability
让 llm-pi-ai 每条路由的模型列表始终跟上端点实时列表:新上线的模型立刻可选,无需重启;缺失的上下文/输出上限从 models.dev 注册表和同尺寸的兄弟模型补齐;图像输入能力借用任何其他已注册服务商,只要它声明了相同 id 且支持多模态;还能强制某个模型支持视觉或仅支持文本。这部分原本是 dsh-plugin-quota-badges 的「模型能力」章节,现在完整搬到这里——另一个插件不再包含它。
工作原理与边界:
- 发现包装:包装运行时的 llm-pi-ai 模型发现,让 GUI 的「获取可用模型」返回实时列表,并与已安装目录合并,而不是只返回目录本身。包装覆盖请求中提到的每一条 llm-pi-ai 路由,不只是 modelsRouteKey:宿主对目录路由的应答直接来自已安装目录,不发任何网络请求,所以厂商的新模型在其他路由上本来会一直不可见。每条路由各自解析自己的端点(请求草稿 → 路由存储的 baseURL → modelsRouteBaseURLs 覆盖值;主路由还会回退到 modelsBaseURL)、凭据(草稿 key → 通过凭据服务取路由的 apiKeyEnv,再取环境变量;主路由还会回退到 modelsApiKey)和 models.dev 目录(主路由用 modelsRegistryProvider,其他路由各用自己的 key)。路由解析不出端点,或者既没有凭据也没有部署头,就保留宿主的应答——包装绝不猜 URL,也不去探测一个注定 401 的地址。端点必须在宿主自己的发现运行之前解析完:某个服务商 pi-ai 没有附带目录(clinepass,以及适配器不认识的任何网关),当请求没写 baseURL 时,宿主会抛 DISCOVERY_FAILED——而 GUI 的请求只写路由。包装以前先调宿主,于是这个错误在端点还没补上之前就冒了出来,路由只能返回编辑器里存的东西。现在包装先解析端点,遇到这种拒绝就用解析出的端点重试宿主(宿主的应答就是该端点自己的列表,所以直接复用并标记为实时,不再探测第二次);只有完全解析不出端点时,宿主的错误才原样向上抛。路由拉不到模型时,用 DSH_TOOLKIT_DEBUG=1 诊断:包装会把走的分支(无端点 / 无凭据 / 探测结果 / 宿主拒绝 / 复用宿主列表)打印到 stderr,只打印凭据长度,绝不打印凭据值。包装会打上 enrichedByToolkit 标记:只有探测真正成功的应答才算同步路由的实时列表,所以探测临时失败会回退到目录,绝不会让已存的路由缩水。卡片上没有「同步模型列表」按钮:采纳新模型在 dsh 自己的模型设置里做(点「获取可用模型」;列表由你确认后才会写入),所以不会有东西把整个实时列表误灌进路由。做这件事的服务端路由——POST /api/toolkit/sync-models——仍然注册着,只在被显式调用时运行(API/脚本);它依然只针对 modelsRouteKey。
- 可搜索的模型选择器:强制视觉 / 纯文本字段不再是逗号分隔的 id 输入框。每个字段都是可按输入过滤的选择器,候选来自该路由已知的模型——点击候选项即可将其添加为 chip,点击 chip 上的 × 可移除,按 Enter 可添加列表里没有的 id,按 Backspace 删除最后一个 chip。候选项来自 GET /api/toolkit/models(modelsRouteKey 已存储的行 ∪ 运行时的 listModels,去重并排序,仅进程内读取——不走网络),每次打开对话框只拉取一次:它只读取已配置的模型,不做任何改动。两个列表互斥:往一个里添加,就会把该 id 从另一个里移除。选择器和两个强制列表都只作用于 modelsRouteKey,与多路由发现包装不同。
- 启动自愈:预先写入路由的传输协议(api),这样配置界面自身的保存也能通过可用性检查,并修复已保存模型上的图像输入(那条保存路径不发送 input 字段)。对强制视觉 / 纯文本列表的编辑会立即作用于已存储的模型——无需重启,无需重新同步。
- 单向迁移:首次启动且 modelsApiKey 为空时,原 quota-badges 命名空间会捐出它的 OpenCode key、强制视觉 / 纯文本列表和路由形态;迁移标记在同一次写入中落盘,所以之后再清空 key 也不会被重新填回。
- 在没有 settings / llm / webServer 接缝的主机上处于休眠状态——而不是坏掉——并且一切都留在这个包内:不改动 dsh 源码。
| 配置 | 默认值 | 含义 |
| optimizations.modelCapability | true | 发现包装、同步路由和能力自愈的总开关。 |
| modelsApiKey | "" | OpenCode API key;为空时回退到环境变量。 |
| modelsApiKeyEnvVar | OPENCODE_API_KEY | key 为空时查询的环境变量。 |
| modelsRouteKey | opencode-go | 主路由:同步路由、选择器和强制视觉/纯文本列表都作用于它;发现包装仍以 modelsBaseURL/modelsApiKey 作为回退。 |
| modelsBaseURL | https://opencode.ai/zen/go/v1 | 为主路由的实时列表探测的端点(其他路由各自使用自己存储的 baseURL)。 |
| modelsRouteApi | openai-completions | 预先写入主路由的传输协议(其他路由使用自己的 api,默认 openai-completions)。 |
| modelsSyncPath | /api/toolkit/sync-models | 同源显式同步路由(没有卡片入口;仅限 API/脚本;改动需重启;只同步主路由)。 |
| modelsPath | /api/toolkit/models | 同源路由,列出选择器的候选项。服务端只注册一次该路径(改动需重启),而卡片会实时重新读取它,所以在重启之前,编辑会让卡片指向服务端尚未提供的路由。 |
| modelsRouteBaseURLs | {} | 按路由覆盖端点,给目录里某个路由补上可探测的端点——该路由的配置档没写 baseURL(比如 minimax-cn、kimi-coding):可以只给端点字符串,也可以给 { baseURL, api },用于配置档里没写明协议的路由(比如给 Anthropic 兼容厂商写 api: anthropic-messages)。优先级低于路由自己存的 baseURL。 |
| modelsRouteExtraModels | {} | 按路由声明端点会提供但从不列出的模型 id,例如 { minimax-cn: [MiniMax-M3.1-Flash-Preview] }。同一个凭据下,MiniMax 对这个 id 返回 200,而 /anthropic/v1/models 只列出八个非 preview 的 id,所以探测永远发现不了它。声明的 id 会追加到该路由的发现结果里(排在端点自己列出的内容之后,已存在的 id 不重复),无论实时探测有没有跑过。 |
| modelsEnrichFromRegistry | true | 从 models.dev registry 补齐缺失的能力与模态。 |
| modelsRegistryProvider | opencode-go | models.dev registry 里的服务商目录。 |
| modelsVision | [] | 强制接受图片输入的模型 id。 |
| modelsTextOnly | [] | 强制丢弃图片输入的模型 id。 |
| modelsTimeoutSec | 10 | 实时探测每次请求的总超时秒数(响应头和响应体)。读 models.dev registry 用自己固定的 4 秒预算。 |
注意:这项优化自带服务端路由和自己的配置字段,所以已经满足下面的毕业规则;目前它有意留在 Toolkit 里,作为一个编号优化项。
smartReply
在输入框内以灰色幽灵文本显示的回复建议,按 Tab 接受。一轮结束后,最后一条助手消息会交给一个小模型,让它起草几句你接下来可能说的话;第一句内联显示,↑/↓ 切换其余候选,Esc 关闭,Tab 把当前选中的填进草稿。
它的工作方式与边界:
- 只在真正有用时出现。 输入框为空且当前空闲时,幽灵文本才渲染——提交过程中不显示,你已经打了字也不显示。输入任意字符立刻关闭。
- 对齐靠测量,不靠猜。 输入框是 Lexical contenteditable,它的 padding、字体、行高、字距都来自 dsh 自己的 CSS。幽灵层复制文本框的计算后几何与排版,定位在实测盒子上,所以 CSS 变了也不会错位。幽灵文本显示期间,输入框自己的 placeholder 会像 dsh 处理 IME 输入时那样让位——只加一个标记属性,幽灵文本消失时再移除。
- 接受路径是公开路径。 Tab 走的是会话服务的 inputActions.setDraft(语音输入用的也是同一个 action),所以只算一步撤销,也从不碰编辑器内部。只有在输入框获得焦点且有建议正在显示时,Tab 才会被捕获,因此页面上其他 Tab 的含义不会改变。
- 每轮一次调用,按 (session, summary) 缓存。 调用失败会解析为「无建议」,同样被缓存,所以端点挂掉时每轮只花一次请求,而不是每敲一个键一次。切换会话或重连会清空缓存,因此上一段对话的 summary 绝不会出现在下一段里。
- 路由通过 modelCapability 自己的解析器解析。 resolveRouteEndpoint / resolveRouteApiKey 正是为此从 model-sync.js 导出的,所以建议会送到你的配置档已经配好的服务商。调用宁可放弃也不猜:没有可解析的端点、没有凭据、路由不支持 openai-completions、或者没有模型,都会得到「无建议」,而不是发出一个坏请求。
- transcript 不受影响。 累加器是一个只有状态、没有视图目标的会话定义,所以它什么都不渲染;原有的助手气泡行为和以前完全一样。
- 和这里的其他优化一样是非侵入式的:只用公开插槽和公开 input action,不改 dsh 源码。
| 配置 | 默认值 | 含义 |
| optimizations.smartReply | true | 幽灵文本和建议调用的总开关。 |
| suggestRouteKey | "" → modelsRouteKey | 建议调用使用的 llm-pi-ai 路由。留空则复用 modelCapability 主路由。 |
| suggestModel | "" | 用来起草候选回复的模型。留空则在调用时解析为路由自己的第一个 catalog id。建议每轮都会跑——请指定一个便宜、快速的模型。 |
| suggestPath | /api/toolkit/suggest-reply | 客户端 POST 到的同源路由(只注册一次;改动它需要重启)。 |
| suggestLanguage | "" | 回复语言固定项;留空则每次请求跟随 UI 语言。 |
| suggestCount | 3 | 每轮保留的候选回复数。 |
| suggestTimeoutSec | 12 | 每次请求的总超时秒数,含 headers 和 body。 |
配置参考
所有字段都在 toolkit 设置命名空间下。组合默认值声明在 cordis.patch.yml,schema 在 index.js,公开类型在 index.d.ts。
| 字段 | 类型 | 默认值 | 使用者 |
| optimizations.workspacelessChat | boolean | true | workspacelessChat |
| optimizations.editLastMessage | boolean | true | editLastMessage |
| optimizations.viewActivity | boolean | true | viewActivity |
| optimizations.slashI18n | boolean | true | slashI18n |
| optimizations.changeReport | boolean | true | changeReport |
| optimizations.opencodeSession | boolean | true | opencodeSession |
| optimizations.modelCapability | boolean | true | modelCapability |
| optimizations.smartReply | boolean | true | smartReply |
| modelsMigratedFromQuotaBadges | boolean | false | modelCapability —— 内部单向标记,在旧版 quota-badges 模型设置被采用时写入;不要手动设置。 |
| chatWorkspacePath | string | "" → <DSH_HOME>/chat | workspacelessChat |
| chatWorkspaceTitle | string | 通用对话 | workspacelessChat |
| modelsApiKey | string | "" | modelCapability |
| modelsApiKeyEnvVar | string | OPENCODE_API_KEY | modelCapability |
| modelsRouteKey | string | opencode-go | modelCapability |
| modelsBaseURL | string | https://opencode.ai/zen/go/v1 | modelCapability |
| modelsRouteApi | string | openai-completions | modelCapability |
| modelsSyncPath | string | /api/toolkit/sync-models | modelCapability |
| modelsPath | string | /api/toolkit/models | modelCapability |
| modelsRouteBaseURLs | Record<string, string \| {baseURL,api?}> | {} | modelCapability |
| modelsRouteExtraModels | Record<string, string[]> | {} | modelCapability |
| modelsEnrichFromRegistry | boolean | true | modelCapability |
| modelsRegistryProvider | string | opencode-go | modelCapability |
| modelsVision | string[] | [] | modelCapability |
| modelsTextOnly | string[] | [] | modelCapability |
| modelsTimeoutSec | number | 10 | modelCapability |
| suggestRouteKey | string | "" | smartReply — empty reuses modelsRouteKey |
| suggestModel | string | "" | smartReply — empty uses the route's first catalog id |
| suggestPath | string | /api/toolkit/suggest-reply | smartReply |
| suggestLanguage | string | "" | smartReply — "" follows the UI language |
| suggestCount | number | 3 | smartReply |
| suggestTimeoutSec | number | 12 | smartReply |
Development
sh
npm test # node --test tests/.test.js — no host required
npm run smoke # host-side smoke for index.js + model-sync.js (needs the peers, see below)
npm run smoke:client # offline smoke for the client bundle (no host required)
npm test and npm run smoke:client are self-contained. npm run smoke imports
index.js, which imports the @deepseek-ai/schemastery peer; that is not a
dependency of this package, so run it from a checkout where it resolves (a dsh
workspace, or a profile with this package installed) rather than from a bare
npm install:
sh
cd /root/deepseek-harness && node /root/dsh-plugin-toolkit/scripts/smoke.mjs
Why @deepseek-ai/dsh-settings is no longer imported: that package's export
list moves with the host build. Older builds exported installSettingsSection /
settingsNamespace; the current checkout exports only
{ SettingsConflictError, SettingsForms, redactSecrets }. A named import of a
missing export is a link-time SyntaxError — it takes the whole plugin down before apply runs (no fiber at all), which shows up as the settings card, the sync route and the model-discovery wrap all silently disappearing. The plugin now calls the runtime service (sctx.settings.register(...)) directly and carries its own settingsNamespace() validator, so it imports nothing from that package.
| Path | Contents |
| index.js / index.d.ts | Host half: settings namespace, chat directory, OpenCode fetch wrap. |
| model-sync.js | Host half: discovery wrap, sync/listing routes, enrichment and migration; also exports the endpoint/credential resolvers smartReply reuses. |
| models-core.js | Pure merge/enrich/diff helpers (no network, no settings access). |
| suggest-api.js / suggest-core.js | Host half: the suggestion route, and the pure prompt-building / JSON-salvage logic. |
| client.js | Browser half: the settings card and every client-side optimization. |
| cordis.patch.yml | Composition defaults inserted into the dsh bundle patch. |
| tests/ | Unit tests: the pure core, the sync route, the settings card, and index.js (the fetch wrap + llm/stream session chain). |
| scripts/ | Smoke, e2e and verification scripts. smoke.mjs / client-smoke.mjs are offline; e2e-verify.mjs (TOOLKIT_VERIFY_URL, DSH_AUTH_PASS) and verify-opencode-session.mjs need a live host. |
| docs/assets/ | The SVG diagrams used by this README family. docs/initiative-.md are internal design notes and are deliberately excluded from the npm package. |
Graduation rules
An optimization graduates out of this package into its own dsh-plugin-* when any of these holds:
- it grows its own settings GUI beyond one toggle row, a service route, a background job, or a client.js feature surface;
- it needs to be enabled/disabled per composition row (Cordis toggles whole rows, not single tools);
- its definition plus tests exceed roughly 200 lines;
- it is worth publishing or sharing on its own.
Model experience
No tool is registered and no prompt surface changes; this package is pure UI/runtime ergonomics. The one exception is the editLastMessage rewrite, which by design changes what the model sees: after an edit, the erased tail is removed from the derived request history (via the surface replace), so subsequent turns read only the edited content.
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
kolawong/dsh-plugin-toolkit
DeepSeek Harness 个人工具箱:可运行时开关的体验优化,每项在工具箱设置页里就是一张卡片。
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,288已汉化MCP
71.7
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9
原生编程智能体paean-ai/deeptide
由 DeepSeek 打造,为 DeepSeek 而生。
★ 1,107已汉化DSH
67.7
多引擎接力台emo-xiaoyu/harness-mix
macOS/Linux 已加入源码构建与启动适配;目标系统的完整桌面验收仍需在对应机器执行。安装方式、Linux…
★ 419已汉化DSH
66.8
这是你的仓库?认领条目