新增 subagent_routed 工具:从任意会话发起一次性子 Agent,并完整挂载到任意 agent preset 上运行,支持按次覆盖模型/提供方,并在调用前预检模型可用性。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:bpc-oss/dsh-routed-subagent
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
一个 DeepSeek Harness 全局插件:让任意会话都能派一个完整挂载到任意 agent preset 的一次性(one-shot)子代理,支持按次指定模型/provider 和模型可用性预检。
官方 subagent / subagent_fork 工具强制子代理继承父方 preset。本插件用自定义 subagent provider 替代:其 async 子代理 setup 调用 agentPresets.mount(childCtx, <preset>)——子代理获得目标 preset 的完整组装(persona、提示词段、技能目录、工具),而不是 persona 拷贝。
特性
任意 preset、任意会话:注册在 host 平面(全局层),所有 preset 的会话都有该工具;新增 preset 零配置。
运行中实时进度:后台子代理暴露
readOutputhook——job_output 读 job 时返回实时快照(耗时/idle、事件数、最近的工具/步骤/文本),2 分钟无新事件时标记「可能卡住」,据此判断方向并及时 job_kill。完整挂载:子代理运行在目标 preset 的 standing 组装下(身份、使命段、技能、工具全用目标 preset 的)。
按次指定模型:
model/provider参数把子代理的 LLM 调用路由到与当前会话不同的模型(走官方resolveChildAgentOptions通道)。模型预检:无效模型快速失败并列出该 provider 的候选模型,而不是等到子代理晦涩地失败。
官方子代理生态:one-shot 生命周期事件、UI 行、轨迹可见;返回子代理最终输出。
provider 注册幂等:多 preset 并存不会重复注册 host 平面 provider。
外部引擎(engine=...)
subagent_routed 支持把子代理派发给外部 CLI agent,而不只是 DSH 内的 preset 挂载。默认 engine 为 dsh(本插件内核),可选:
| engine | 驱动 | 后台 job | 实时进度 | kill | 指定模型 | continuable |
|---|---|---|---|---|---|---|
dsh(默认) |
preset 挂载 provider | ✅ | ✅ | ✅ | ✅ | ✅ |
codex |
codex CLI app-server --stdio |
✅ | ✅(进程事件流) | ✅ turn/interrupt |
✅ thread/start model |
✅ 同 thread 续话 |
claude |
Claude Code SDK | ✅ | ✅ | ✅ abortController/close |
✅ | ⚠️ 仅官方 Anthropic API |
codebuddy |
CodeBuddy Code CLI --print |
✅ | ✅(NDJSON stream) | ✅ 进程终止 | ✅ --model |
✅ --session-id / --resume |
// 外部 codex 子代理(后台、指定模型、实时进度)
await subagent_routed({
engine: 'codex',
provider: undefined, // 外部引擎忽略 DSH provider
model: 'gpt-5.6-sol', // codex thread 显式模型
prompt: '...',
run_in_background: true,
})
- codex 引擎:长驻一个
codex app-server --stdio进程(懒启动、init-once),每个 run 用thread/start+turn/start,实时进度取自item/agentMessage/delta事件,kill 用turn/interrupt,continuable 复用磁盘持久化 thread(同一threadId续话)。无人值守默认approval_policy: never。codex CLI 必须已登录(codex login)。 - codebuddy 引擎:spawn
codebuddy --print --output-format stream-json --include-partial-messages --dangerously-skip-permissions,实时进度取自text_delta事件,continuable 用--session-id <uuid>建会话 +--resume <uuid>续话(session 磁盘持久化)。默认模型hy3(可用config.codebuddyModel/$CODEBUDDY_MODEL覆盖)。CodeBuddy Code CLI 必须已安装(codebuddy --version)。 - 启动入口:优先
CODEX_BIN环境变量(可指向原生codex.exe或bin/codex.js),否则自动探测 npm 全局@openai/codex/bin/codex.js。 - claude 引擎:驱动
@anthropic-ai/claude-agent-sdk,模型、kill、进度、后台均可用。⚠️ continuable 依赖官方 Anthropic API——当 claude CLI 配置为自定义后端(如AnthropicBaseURL指向第三方/本地)时,sessionId + persistSession可能卡死/不可用,需AnthropicBaseURL指向官方 API 才能可靠续话。
分发
仅 GitHub。本插件不发布到 npm,通过挂载包目录安装(见下)。peerDependencies 以真实 semver 声明、仅作元数据,不参与 npm 解析。
兼容性:目标为 DeepSeek Harness rc.7+(行为已对照 rc.7 源码核实,并在 rc.8 运行时验证)(本插件依赖的 async 子代理 setup 是较新的 harness 行为)。
安装
纯 ESM 包,带 cordis.patch.yml bundle 声明。
1. 把包链接进 harness 安装
插件静态 import @deepseek-ai/* 包(Node ESM 按 realpath 解析)。在包目录创建指向 harness 安装的 node_modules junction/软链:
:: Windows
mklink /J "<plugin-dir>\node_modules" "<harness>\resources\host\node_modules"
# POSIX (Linux/macOS)
ln -s "<harness>/resources/host/node_modules" "<plugin-dir>/node_modules"
2. 把 bundle 加进 profile
把包加进 profile 的 dsh.profile.bundles 列表(如 <dshHome>/profiles/web/package.json):
{
"dependencies": { "dsh-routed-subagent": "link:<plugin-dir>" },
"dsh": { "profile": { "bundles": ["...", "dsh-routed-subagent"] } }
}
仓库里的 cordis.patch.yml 就是注册插件的 bundle 层;包被列入 bundles 时自动应用。
提示:若你的部署提供热装配工具(如 super-injector 风格的
dev_install_package(dir=...)),可代替上面的手动步骤;重启后两种方式都由bundles列表自动装配。
用法
三种形态(一个工具):
| 模式 | 用法 | 返回 |
|---|---|---|
| 后台 one-shot(默认) | run_in_background: true(默认) |
立即返回 job id;job_output(实时进度)收 / job_kill 停;中止主对话不影响子代理 |
| 前台 one-shot | run_in_background: false |
阻塞到子代理返回最终输出 |
| fork | fork: true |
子代理继承本对话已完成轮次(上下文),再挂载目标 preset |
| continuable | continuable: true |
返回持久 subagent id;之后用 send_message(subagentId, ...) 续话;子代理挂载目标 preset 并在续话/重启后保持 |
subagent_routed(prompt="用 dev 标准审查本仓库", preset="dev", description="dev 审查") # 后台 one-shot
subagent_routed(prompt="继续审查", preset="dev-reviewer", description="跟进", fork=true) # 继承本对话
subagent_routed(preset="dev", prompt="审计本仓库", description="审计", continuable=true) # 之后 send_message 续话
平台补丁(continuable + preset 挂载)
continuable 模式让子代理挂载目标 preset 并在续话/重启后保持——需要对开源的 @deepseek-ai/dsh-subagent 做纯增量补丁:
- 安装级 junction(唯一装配点):
resources\host\node_modules\@deepseek-ai\dsh-subagent→ 补丁 fork<fork-dir>\@deepseek-ai\dsh-subagent(纯净原包备份于<fork-dir>\@deepseek-ai\dsh-subagent.orig);插件启动断言在补丁未生效时 fail loud;不要给@deepseek-ai/dsh-subagent加 profile-locallink:(会分裂模块身份、补丁失效) - 补丁面(只做加法,无 preset 的官方路径逐字节不变):
applyChildComposition的composition.preset挂载目标 preset(跳过 composeFrom 防双绑定;保留 delegation/persona/toolFilter)+ appendagent-preset/selected(target)(防 fork seed 遮蔽 header);materializeTrackedsetup 变 async(工厂 await,保持{commit}契约);continuable descriptor 加可选preset(continuable 版本 2→3,one-shot 保持 2——回滚时 v3 干净 NOT_RESUMABLE,旧 v2 仍可解析);coldResume从descriptor.preset重建同一 preset,preset 缺失时报指名错误 - 回滚:删安装 junction、把
<fork-dir>\@deepseek-ai\dsh-subagent.orig复制回安装位、重启——官方行为恢复(已建的 preset-continuable 子代理按设计 NOT_RESUMABLE)。注意:若其他 profile 树(如dsh-continuous-worker\node_modules\@deepseek-ai\dsh-subagent)仍指向 fork,需一并处理避免悬空
禁用官方 subagent
全部官方能力移植完成后,subagent_routed 成为唯一委派入口:全局 tools.guard 在执行期拒绝 subagent / subagent_fork 并引导到 routed(config.disableStockSubagent ?? true;设 false 保留)。范围:挂载本插件行的 preset。
subagent_routed(
prompt="用 dev 工程师标准审查这个仓库",
preset="dev", # roster 中的任意 preset id
description="dev 审查", # 显示名
max_depth=2, # 递归预算(默认 3;正整数 >= 1)
run_in_background=true, # 默认 true:后台 job,主对话继续
model="deepseek-v4-flash-free", # 可选:子代理本次使用的模型
provider="opencode", # 可选:该模型所属 provider
)
| 输入 | 行为 |
|---|---|
preset 无效/无法解析 |
报错并透传 roster 可用 preset id |
model 在 provider 下无效 |
快速失败,列出该 provider 的候选模型(原始错误保留在 cause) |
model 省略 |
子代理继承当前会话模型(向后兼容) |
| max_depth 非正整数 | 工具层校验报错 |
| un_in_background(默认 true) | 立即返回后台 job id;job_output 收结果 / job_kill 停止;中止主对话不影响子代理 |
| 正常调用 | 子代理完整挂载目标 preset,跑一轮,返回最终输出 |
原理
- 自定义 subagent provider(
routed-mount)复刻官方 one-shot 进程内驱动(dsh-subagent-in-process-driver的startInProcessRun),唯一关键改动:子代理 setup 改为async并await agentPresets.mount(childCtx, targetPreset)(替代认父)。 agents.create会 await setup(dsh-agent-loop已确认)——async mount 在未发布的创建窗口内执行,失败整体回滚。- 子代理会话 header 记录
agentPreset: <目标>(覆盖父方值)——冷读按真实运行的组装重建。 - 工具分两段顺序收尾:先 result 后 dispose(与官方
settleForegroundRun顺序一致)——若并行会把 dispose 的取消标志抢先,导致子代理"一启动就 aborted"。
已知限制
- preset generation drift(已知限制):
mount在每次创建/续话时按 id 重解析 preset——两次续话之间编辑 preset 文件会让后续轮次挂到该 preset 的新一代(官方composeFrom加入父的 standing 实例、不重解析)。文档化行为;保持 preset 不变即可维持轮次一致。 - 失败语义:与官方前台 subagent 工具一致——子代理以
error/refusal/max-tokens结束时工具调用抛错(附部分输出);仅completed与调用方取消的aborted作为返回值。底层 LLM 错误细节见子代理会话日志。 - 预检是有条件的:仅当 harness 暴露
llm服务且存在 provider 路由(显式provider或继承父方)时才执行预检;否则跳过、直接派发。 - provider 可用性取决于环境:预检只校验模型目录;真正调用仍需 provider 可达且 key 有效。
开发
node --check lib/index.js # 语法检查
插件为单个 ~350 行文件、零构建。CI 每次推送执行 node --check。
License
MIT
链接
同类插件
Tencent/WeKnora#dsh-weknora★ 21359
把 WeKnora 知识库接入 dsh 的四个只读工具:列出知识库、混合检索原文片段、按顺序还原单篇文档,以及直接取用 WeKnora 自己带引用的 RAG 或 ReAct agent 回答(含可续聊的 session id)。
superdesigndev/treg★ 1189
给 Agent 的工具目录:按「要做的事」检索约 2,600 个外部接口(SEO 与 SERP、外链、社交、人物与公司信息补全、广告库、抓取),查看参数与单次调用价格后直接调用,凭据由服务端注入。附带技能,MCP 行在未设置 TREG_TOKEN 前保持禁用。
anysearch-team/anysearch-dsh★ 399
基于 AnySearch 的实时网页与垂直搜索插件,为 DeepSeek Harness 提供搜索工具。
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 379
面向 DeepSeek Harness 的本地 IMAP 发票下载、OCR 识别、归档与 Excel 报销汇总。
omdsh-dev/dsh-data-agent★ 184
让 AI 帮你连数据库、写 SQL。
zhaoolee/notes#dsh-plugin★ 158
将 DSH 对话导出为锤子便签风格 PNG,或在配置的账号工作区中新建和更新 Markdown 便签。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。