ivanon/dsh-dev-crew0

dsh-dev-crew

A DeepSeek Harness plugin that mounts one subagent delegation tool per configured role and model, each bound to its own model provider route.

包名
dsh-dev-crew
版本
0.1.0
许可证
MIT
最近更新
2026年8月19日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:ivanon/dsh-dev-crew

配置

在 profile 的 cordis.patch.yml 中配置角色。每个角色可绑定一个或多个模型, 每个模型对应一个独立的委派工具。

- id: dsh-dev-crew
  config:
    roles:
      - id: implementer
        enabled: true
        models:
          - alias: default
            provider: kimi-coding
            model: k3
      - id: reviewer
        enabled: true
        models:
          - alias: ds
            provider: deepseek-official
            model: deepseek-v4-flash
          - alias: kimi
            provider: kimi-coding
            model: k3

工具名规则:单模型角色为 subagent_,多模型角色为 subagent__。上面的配置会挂出 subagent_implementersubagent_reviewer_dssubagent_reviewer_kimi 三个工具。

provider 必须是 Models 设置页中已就绪的路由。未配置或不存在的路由不会挂载 工具;插件会通过 ctx.logger 记录一行说明原因的警告,但该警告在当前 headless 一次性执行模式下不会打印到终端(见「已知限制」)。

内置三个角色 implementer / reviewer / researcher,各自带有写好的 persona 与工具范围,默认全部停用 —— 因为路由取决于你自己配置了哪些 provider。

用这三个 id 时只需写 id + models + enabled:未提供的 personatoolFilter 由插件按 id 用内置模板填充。显式写出则以你的值为准,且是整体替换 而非与内置值合并 —— 自己写 toolFilter 就要把想保留的 deny 项一并列出。非内 置 id 不做填充。

无论是否填充,每个角色的子代理都看不到本插件挂出的其他委派工具:插件会把本次实 际挂上的其他 subagent_* 工具名追加进该角色的 deny,避免子代理沿角色链继续 向下分派。

两个角色用同一个 id(或同一角色内两个模型用同一个 alias)会推出重名工具,插 件在挂载前直接报错,不会挂上一个卸不掉的实例。

配置支持热更新:宿主组合了 settings 服务时,本插件会在 dsh-dev-crew 命名空间下注册用户设置文档,roles 等字段的变更会即时同步到已挂载的工具集, 无需重新加载插件。未组合 settings 服务的部署(例如 headless)不受影响,继续 使用 profile 里的入口配置。

gate.enabled 是例外:它只在插件挂载时决定是否注册纪律 guard,中途通过设置 页开关不会生效,需要重新加载插件才能应用。gate.plansDir 不受此限制,热更新 后立即生效。

HTTP API 与配置界面

宿主组合了 @deepseek-ai/dsh-host-webserver(即 ctx.get('webServer') 非空,通常 是 Web host,headless 部署没有这一层)时,插件在 /crew/api 前缀下注册三条路由:

路由方法说明
/crew/api/healthGET返回 { mounted, skipped }:已挂载的委派工具名与被跳过的路由及原因,补上 headless 下 logger.warn 不可见的可观测性缺口
/crew/api/settings.getPOST返回 { config, revision }:脱敏后的当前配置与 revision
/crew/api/settings.updatePOSTbody 为 { config, expectedRevision },走 settings.update() 的 merge-then-validate;revision 冲突返回 409 REVISION_CONFLICT

三条路由都要求 Host 头精确匹配回环地址(localhost / 127.0.0.1 / [::1] / ::1)或部署显式配置的 trustedHosts(当前插件把它硬编码为空数组,尚无 schema 与界面绑定),不匹配一律 403 UNTRUSTED_HOST这只是主机名白名单,不是 CSRF 防护:Host 头检查挡不住浏览器页面向 localhost 发起的跨站 POST,当前定位是 「仅面向本地信任环境」,请勿在暴露给不受信任网络的部署上依赖它。

配套的客户端配置界面(src/client/CrewSection.tsx,通过 dsh.client.inject 挂进 settings.section)走同一套 HTTP API 读写配置、展示角色列表与健康状态。

这两条能力都需要 Web host:在 headless 部署下(例如本仓库用于验收的 crewtest profile),组合树里没有 webServer 服务,三条路由与配置界面都不存在 ——这是 headless 部署形态本身的限制,不是 bug。

在组合了 Web host 的部署下,路由注册走的是 ctx.inject(['webServer'], hctx => { hctx.effect(() => registerCrewApi(hctx, {...})) })registerCrewSettingssettings 服务同理):ctx.inject 会新建一个只在 webServer 服务就绪后才执行 的子插件,服务缺失或还没轮到时子插件停在 PENDING、主插件不受影响,服务就绪后 自动执行——不依赖 webServer/settings 是否已经在 apply() 执行的那一刻存在。 webServer/settings没有写进主插件顶层的 inject 数组,因为那样会让 整个插件在 headless(不组合这两个服务)下永久 PENDING

已在新建的 crewtestweb profile(['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app', 'dsh-dev-crew'])上端到端实测通过,包括 settings.update 的写入 + revision 递增 + 回读一致:

$ curl -s localhost:3099/crew/api/health
{"ok":true,"value":{"mounted":["subagent_implementer"],"skipped":[]}}
$ curl -s -X POST localhost:3099/crew/api/settings.get
{"ok":true,"value":{"config":{...},"revision":0}}
$ curl -s -H 'Host: evil.com' localhost:3099/crew/api/health
{"ok":false,"error":{"code":"UNTRUSTED_HOST","message":"request rejected by the plugin trust fence"}}

(第三条正确返回 403。)连续重启该 profile 三次复测,三条路由每次都正确注册, 排除了偶发时序窗口的可能。

客户端配置界面(CrewSection.tsx)的实际浏览器渲染(三态健康显示、保存失败时 表单不丢内容等)未做人工可视化验证,只验证了它依赖的 HTTP API 契约;见「已知 限制」。