dsh-teammate-model-picker
给 DeepSeek Harness(DSH)的队友会话加一个模型选择器。
队友因为额度耗尽而失败之后,你可以直接给它换一个有额度的模型,然后让它继续跑 —— 不必注销重建队友,也不影响你自己的会话或全局默认模型。
它解决什么问题
DSH 的队友(teammate / 可继续的 subagent)在被派发的那一刻就把当时 Lead 使用的模型写进了自己的会话描述(durable descriptor)。此后每次唤醒 —— 收到消息、恢复运行、冷恢复 —— 都按这份描述里的模型路由,不会重新解析当前默认模型。
于是当队友被钉在一个已耗尽额度的模型上时:
- 你在 GUI 里切换自己的模型,它不受影响;
- 给它发"继续",它会在同一条已耗尽的路由上再失败一次。
而 DSH 现有的"切换模型"能力对队友会话是明确拒绝的:Client 侧官方选择器对 subagent 会话直接返回空,Host 侧通用 Session 路由对 origin: "subagent" 的会话一律返回 session/agent-busy。队友会话的模型座位因此是空的。(为什么拒绝是三层叠加的、缝隙在哪里,DESIGN.md 有完整论证。)
它做了什么
一个 DSH 插件 bundle,分两半:
- Host 半边:把"每个队友的模型覆盖"存成插件 Config(
overrides),在队友 Agent 发布时(agent/created)为它安装与官方installModelSelection等价的三个 waterfall,覆盖agent/request的最终路由。 - Client 半边:在 composer 工具行的
conversation.input.right座位注册一个模型选择器,观感对齐官方选择器(两级菜单:模型 / 推理等级)。只在队友会话里渲染。
四条硬约束:不改任何官方包、不引入新 session 事件类型、没有 override 时零介入(派发路由原样生效)、不动全局默认模型。分层与依赖方向见 DESIGN.md。
安装
plugin_manager install_bundle target = <本目录绝对路径>
返回 application: applied 且 warnings: [] 即生效。卸载:
plugin_manager remove_bundle target = @wufeng0105/dsh-teammate-model-picker
三个层面要分清:安装/卸载 bundle 改的是 profile 的
package.json里dsh.profile.bundles;bundle 默认在源cordis.patch.yml里;用户配置(你选的模型)在用户 profile patch 层的- id: teammate-model-picker这一行。清空用户配置 ≠ 卸载 bundle。
使用
- 打开一个队友会话。
- composer 工具行右侧会出现模型选择器(显示当前模型 + 推理等级)。
- 点开 → 「模型」→ 选一个还有额度的模型。
- 给队友发"继续"。
改动立即生效,并写入用户 profile patch 层,重启 Harness 后依然生效。菜单里的「恢复派发路由」会删掉这个队友的 override,让它回到派发时钉下的那条路由。
Config.showPicker 是真正的开关:设为 false 时选择器不渲染(默认 true)。
验证
离线门(一条命令,秒级):
npm run check
静态与契约门(身份、依赖方向、样式 token、打包清单、版本区间、文档预算)+ 离线契约测试(Host fake-DSH、Client vm、负例自检)。负例保证每道门都能变红。
环境检查:
node tools/doctor.mjs # 版本区间 / row / 会话日志格式 / schema 协议自述
node tools/doctor.mjs --contracts # 打印本插件依赖的契约清单
行为门(需要真机队友会话):换模型是否真的生效,只能由一条真实会话判定。
node tools/session-model-log.mjs <teammateSessionId>
它解压 session.v4.jsonl.zstd 并打印四类事件:subagent/descriptor(派发时钉下的路由)、model/selection(切换意图)、request/header(每次请求实际用的路由)、turn/end(含失败原因)。判定标准与历史实测见 REPORT.md。
已知限制
- 选择器落在相邻座位,不是官方那个模型座位。视觉接近,但 tooltip/aria 与键盘 Tab 序列与官方不完全同源。
- 复制了官方 CSS(类名前缀
tmp_,只用主题 token)。官方改版样式时需要同步;token 改名只退化外观、不会崩。 - 没有模型白名单:展示的是运行时目录全量。若部署启用了子代理模型白名单,二者可能不一致。
- 切换在活跃 turn 中生效于下一个 step:与官方
installModelSelection语义一致,不中断进行中的请求。 - 对 agent 暴露的操作尚未接线:换模型目前只有 UI 路径,规范要求的 agent tool 还没注册(需要一次运行期 API 查阅)。
与官方的关系
这份实现是本地绕过,不是官方正解:队友会话被排除在模型选择器之外、Host 侧对 subagent-owned 会话的一刀切拒绝、descriptor 不可改写、selectModel 顺带持久化全局默认模型 —— 这几条更适合由 DSH 官方修,官方修复到位后本插件应评估退役。逐条论证见 REPORT.md 的「上游建议」。
仓库内容
公开仓库是本地项目的子集:内部工作文档(决策记录、写作账本、耦合审计、改造方案)不随仓库发布,正文中提到它们的位置在仓库里读不到,属预期。
维护者发布流程(push → Tag → Release → npm,含一致性核对与回滚)见 RELEASE.md。
License
MIT,见 LICENSE。
vendor/ 下的 @deepseek-ai/schemastery 3.18.5 与 @deepseek-ai/cosmokit 1.8.6 为原样内置的第三方代码(MIT),各自保留 LICENSE 文件。之所以内置,是因为 bundle 以 link: 方式挂入 profile 时,裸包导入无法解析。
No comments yet. Be the first to write one.