dsh-weknora
Tencent/WeKnora把 WeKnora 知识库接入 dsh 的四个只读工具:列出知识库、混合检索原文片段、按顺序还原单篇文档,以及直接取用 WeKnora 自己带引用的 RAG 或 ReAct agent 回答(含可续聊的 session id)。
为 DeepSeek Harness 提供后台子代理的主动等待:注册 wait_subagent 模型工具,阻塞等待指定的后台 continuable(可续会话)子代理收尾(settlement),返回停止原因与收尾消息——可选超时、成员资格门控拒绝未知 id、事件驱动零轮询。补齐 run_in_background 发后不管与异步收尾通知之间的缺口:等待后台子代理(subagent wait)不再轮询 list_agents、不再靠猜。
English · 简体中文
A DeepSeek Harness (cordis) plugin that registers a wait_subagent tool: block until a background continuable subagent settles, then get its stop reason and closing message in the same call.
It fills a real gap in the subagent workflow: run_in_background returns a subagentId immediately, but the only way to learn the outcome is an async settlement notice — there was no tool to actively wait for it.
Start a subagent in the background, then wait for it:
wait_subagent({ subagent_id: "session-9f2c47ab-83d1-4e06-b5a9-1c7f2d84e0b3" })The call blocks while the subagent runs. When it settles, the tool returns:
{
"status": "settled",
"subagent_id": "session-9f2c47ab-83d1-4e06-b5a9-1c7f2d84e0b3",
"stop_reason": "completed",
"closing_message": "Spec written to docs/plan.md; 3 files changed, ready for review."
}The text the model sees:
Subagent session-9f2c47ab-83d1-4e06-b5a9-1c7f2d84e0b3 settled: completed
Spec written to docs/plan.md; 3 files changed, ready for review.Other outcomes:
Situation · Result Subagent already settled before the call · { "status": "settled", "subagent_id": "…", "stop_reason": "unknown" } — *"had already settled before this call; its closing message was delivered separately as a settlement notice."* timeout_ms elapsed · { "status": "timeout", "subagent_id": "…" } — *"Timed out waiting for subagent …; it may still be running."* Caller's AbortSignal fired · { "status": "cancelled", "subagent_id": "…" } Unknown / foreign subagent id · Rejected with an error: *"… is not one of your subagents — pass the id a background dispatch returned (see list_agents)"*
What the subagent system gives you today:
subagent with run_in_background: true → returns a subagentId at once; work continues asynchronously and a settlement notice arrives later.subagent with run_in_background: false → blocks at start time only.send_message → fire-and-return, never waits for the answer.list_agents → a snapshot, not a wait (and explicitly "not to poll").What's missing: after starting a subagent in the background, there is no tool to block and wait for it to finish. That is exactly what wait_subagent does — use it when your next action depends on the subagent's result, instead of guessing or polling.
wait_subagent tool on every root agent (same pattern as dsh-proactive).ctx.subagents.listChildren(parent.id) — the same projection list_agents reads — covers live AND storage-only children, so unknown ids are rejected with an error instead of being misreported as settled. A known child absent from the live registry has already settled.subagent/end lifecycle event — which fires when a child's activation is disposed — scoped through the plugin context (an ancestor of all agent contexts).subagent/end in the same synchronous block (finishDisposal), so attaching the listener first and then re-checking the registry is race-free.timeout_ms parameter returns status: "timeout" if the subagent does not settle in time. Omit to wait indefinitely — usually the best choice; if set, use a generous value rather than polling with repeated short waits.AbortSignal (returns status: "cancelled").wait_subagent(subagent_id: string, timeout_ms?: integer)
→ { status: "settled" | "timeout" | "cancelled",
subagent_id: string,
stop_reason?: "completed" | "aborted" | "error" | "max-tokens" | "refusal" | "unknown",
closing_message?: string }wait_subagent is concurrency-safe, but interrupt_agent is not — it needs the tool lane exclusively and queues behind any in-flight call. Issuing wait_subagent and interrupt_agent in one parallel step serializes them: the wait runs to its timeout first, and only then does the interrupt land. Call interrupt_agent first, then wait_subagent in the next step (an already-interrupted child settles quickly).
dsh plugin --profile web add dsh-wait-subagentOr straight from GitHub (source install — plain ESM JavaScript, no build step, so pnpm ≥ 10 build-script approval is not needed):
dsh plugin --profile web add github:john-walks-slow/dsh-wait-subagentRestart your DSH instance after installing; the bundled cordis.patch.yml registers the loader entry automatically.
wait_subagent) on every root agent. No web client, no UI changes, no configuration.subagent/end lifecycle event), not polling; CPU cost while blocked is negligible. An omit-timeout_ms wait is unbounded by design; set a generous timeout_ms when you need a bound.@deepseek-ai/dsh-tools 0.1.2-rc.1 (aligned with the dsh 0.1.2-rc.1 locked version), Node ≥ 22.5.npm installThat's it — lib/ is plain ESM JavaScript; there is no build step and no test suite.
npm run release # bumps the patch version and packs /tmp/dsh-wait-subagent-<newver>.tgzThen publish the tarball to npm and push the version commit and tag:
git push --follow-tagsVerify with npm view dsh-wait-subagent version.
MIT