dsh-session-subagent-models
⚠️ 实验性 / 不保证维护 / 已知设计缺陷
这是一个「能用但设计不正确」的插件,发布目的是先用起来,不是作为范例。 它通过改写宿主内部状态实现功能,违反了下游仓库的两条硬性约束(见 已知设计缺陷)。上游任何相关重构都可能让它静默失效。 不承诺后续维护:issue 和 PR 可能不会被处理。
Per-session editable subagent model allowlists for [DSH].
A DSH plugin that lets you edit, per Session, which provider/model routes a child agent may be spawned with — instead of switching a global setting and having to keep it.
⚠️ Experimental / unmaintained / known design flaws. This plugin works by mutating host internals, violating two invariants of the host repository. It may break silently on upstream refactors. No maintenance commitment.
它解决什么
DSH 的 subagent-model-selection-settings 会在 agent/created 时把全局
allowedModels 快照成一条 subagent/model-selection-policy 会话事件,而
subagentModelSelectionPolicy 投影的折叠是 first-write-wins。因此之后再改全局
设置,对已创建的会话无效 —— subagent / subagent_fork /
list_subagent_models 会持续以
provider "x" is not allowed for this Session 拒绝路由。
本插件按会话遮蔽(mask)那个投影单元,让所有消费者立即看到覆盖值。
功能
- 输入框工具行入口:
子智能体模型 · N,与「完全权限」「模型选择器」同排、同规格。 - 编辑面板:已生效模型以可移除的胶囊列出;支持搜索、按 provider 全选/清空、
「只看已选」筛选;创建时快照的条目带
快照标记;不在目录中的路由单独成组。 - 键盘可用:
↑/↓/Home/End移动游标,Enter切换或保存。 - 字号跟随:面板宽度、行高、控件尺寸全部随宿主的
--dsh-content-font-size偏好缩放,样式表内无硬编码像素字号。 - 模型工具
session_subagent_models(list/set/clear)。 - HTTP API(仅 loopback;写操作要求同源):
GET /catalog、GET|PUT|DELETE /session/<id>、GET /overrides。
截图
| 浅色 · 17px 字号 | 深色 · 快照态 |
|---|---|
![]() |
![]() |
| 写入端未启用 | 无读取方 |
|---|---|
![]() |
![]() |
已知设计缺陷
这一节是本文档最重要的部分。以下不是「待优化项」,而是「设计错误」,记录在此 以便使用者判断风险,也便于将来按正确方式重写。
1. 改写投影活单元 —— 越权访问私有内部
宿主的 ProjectionHandle 公开接口只有 stateOf / readerFor / dispose,并且
stateOf 的文档明确写着:
@returns the projection state at the Session's current cursor.
Do not mutate the live value.
本插件做了三件违反它的事(src/index.ts):
| 行为 | 问题 |
|---|---|
(ctx.sessionProjections as unknown as ProjectionRegistryInternals).registrations.get(...) |
读取非公开字段;registrations / cells 不在任何公开契约里 |
registration.cells.get(session).state = routes |
直接改写活单元,违反上述 "Do not mutate the live value" |
结构复制 subagentModelSelectionProjectionDefinition 以取得 handle |
依赖别人的定义是纯数据;上游改 key / stateVersion / apply 时静默失效而非报错 |
2. 破坏「Model-visible ⟺ logged」不变式
宿主规则:任何进入模型请求的内容都必须能从会话日志重建。
子智能体路由策略是 model-visible 的(它决定模型能给 subagent 传什么),但本插件
把覆盖值放在 <DSH_HOME>/session-subagent-models/overrides.json 这个旁路文件里。
后果:同一个会话日志,换一个进程或删掉该文件,会得到不同的授权结论。日志不再是
唯一权威。
3. 该能力在宿主里没有接缝
按宿主约定,能力接缝应由 Service Definition / Service Provider / Consumer 三个角色
组成。但模型选择策略目前是硬链接:tool-subagent 直接 import 同包内的
subagentModelSelectionPolicy()(index.ts:275、list-models.ts:47)。没有服务键,
第三方插件无位可占——这正是本插件只能去撬内部状态的根因。
4. 对 AgentTeams 无效
两条委派路径都不经过该策略:
- 官方
agent-team:roster.ts调startActivation时不传agentOptions,成员路由 完全继承 Lead;该包根本没有 per-member model 字段。 - 第三方
@nanmicoder/dsh-agent-teams:直接传request.agentOptions = { provider, model, reasoningEffort },走child-agent.ts的resolveChildAgentOptions,同样绕过assertAllowedModelSelection。
所以本插件不能控制 Team 成员的模型。UI 也不应暗示可以。
5. 依赖注入器的槽位白名单
本插件的入口注册在 conversation.input.left。某些第三方注入器(如
dsh-super-injector)维护一份 KNOWN_SLOTS 文本白名单来静态校验注册名;白名单缺项
会导致插件被拒或渲染到错误位置。这是生态层面的脆弱点,不是本插件能控制的。
6. 依赖方缺失时功能空转
若 tool-subagent 在 profile 中为 disabled,策略的三个消费者
(subagent / subagent_fork / list_subagent_models)都不存在,此时改动不会影响
任何行为。面板会显示 暂无读取方 徽章,但不会阻止你编辑——请自行确认。
若 subagent-model-selection-settings 未加载,则没有任何策略可供遮蔽:面板显示
策略写入未启用,保存被禁用,PUT 返回 409。
7. 工程性欠缺
- 只有客户端行为测试(jsdom),宿主侧无测试。
- 文案硬编码中文,未接入宿主的 locale 字典。
overrides.json无版本号与迁移策略;上限 500 条按插入顺序淘汰。
正确的设计应该是什么
供将来重写参考(需要改宿主核心,不是插件能独立完成的):
- 新增服务定义
subagentModelPolicy(resolve(session) => routes | undefined)。 - 官方快照实现注册为
order:100Provider;覆盖实现注册为order:200Provider。 - 覆盖值写入会话事件
subagent/model-selection-override(可反复追加), 投影折叠改为「快照初值 + 覆盖可覆盖」,并升stateVersion。 - 消费者改为调用服务,而非直接 import。
- Team 包按需
inject: { optional: ['subagentModelPolicy'] }自行决定是否接入。
这样才同时满足「一切皆插件」与「日志权威」。
安装
# 1. 构建(需要一个 DSH 源码 checkout 提供 tsc 与依赖)
DSH_CHECKOUT=/path/to/deepseek-harness bash scripts/build.sh
# 2. 注入(运行时,host + UI,可热重载)
# 使用 dsh-super-injector 的 dev_inject_plugin <本目录>
lib/client.js 为手写的 window.__ModuleLoader__.load CJS bundle,无独立构建步骤。
开发
node tests/client.test.mjs # jsdom:挂载真实 bundle,端到端驱动面板
bash dev/preview-build.sh # 生成独立预览页 dev/out/
python3 -m http.server 4173 --directory dev/out
# http://127.0.0.1:4173/index.html?scene=override&theme=dark&font=17
# scenes: snapshot | override | empty | compact | unwatched | unavailable
许可
BSD-3-Clause。




No comments yet. Be the first to write one.