dsh-subagent-codebuddy: DSH bundle for delegating sub-agents to the local CodeBuddy CLI

Preface

Anyone writing a DSH profile will eventually encounter a specific problem: The parent agent needs to hand a task to a child agent, but the child agent runs in an independent CLI process, not an in-process teammate, nor a local child that can continue a conversation. At this point, using a general delegation tool only yields started subagent <id>. The model doesn’t know it has started running and can’t wait for any progress, so it repeatedly asks wait_agent / list_agents. However, these two tools only track in-process teammates and local children that can continue, and they will forever answer “no subagent running” for external one-shot child agents.

The bundle introduced below, lloydzhou/dsh-subagent-codebuddy, is a profile bundle designed for this scenario: It delegates each time to the local CodeBuddy CLI (codebuddy --acp), the model is pinned by configuration, and the tool descriptions and result copy explicitly tell the model “the child agent is already running, don’t wait for it.”

What is this

One-sentence positioning: An independent DeepSeek Harness profile bundle that provides a one-shot (one-shot) child agent provider. Every subtask runs in the local CodeBuddy CLI as an ACP session, and the child model is pinned by configuration (default deepseek-v4.1-flash, passed via --model <id>).

  • Maintainer: lloydzhou
  • Package name: dsh-subagent-codebuddy, published with the same name on npm
  • Version: 0.2.1-rc.5 (see package.json)
  • License: MIT
  • Language: TypeScript
  • Category: Workflow
  • Source attribution: The community directory page is on skillhub.cn, an independent community site; the plugin itself is an open-source repo. The directory site and DeepSeek / Huansuan have no official affiliation.
  • Stars: 0 (as of writing time, from GitHub repo page)

It solves three interconnected problems: Delegation must be able to truly initiate, results must be able to return to the parent session when the child agent settles, and the model should not poll a running process that can never be found due to vague results.

Runtime Behavior: One-shot Delegation and Result Return

The provider is written on the public subagent interface exposed by the harness. Specifically, it uses three helpers from @deepseek-ai/dsh-subagent: NO_START_CAPABILITIES, settleRunResult, and subprocessRunHandle. It does not have prepareContinuable, so there is no inbox on either side: The CodeBuddy child agent is an independent CLI process, has no dsh tools, and cannot callback the parent session.

The result return method is therefore very limited: The child agent reports only once when it settles. The harness injects a Background subagent <id> finished notice, carrying the child agent’s closing message. This notice triggers the next round of the parent agent. There is no progress channel during the run—the README emphasizes this is a backend feature, not a plugin bug.

As a comparison, the README explains that notices like Team message from <child>: … belong to local child agents that are continuable. They come with the dsh send_message tool and the parent session’s inbox. The CodeBuddy child agent does not belong to this category, so such mid-run messages do not appear.

Therefore, the tool’s job becomes: Prevent the parent agent from waiting in vain. Its result copy explains that polling is ineffective and the parent agent should continue doing other things or end the current round, allowing the settle notice to arrive at the earliest step boundary.

Two Tools on the Model Side

The two tools mounted by the bundle both come from the bundle’s own ./tool entry point, not the general @deepseek-ai/dsh-tool-subagent:

  • subagent_codebuddy: Starts a run once
  • codebuddy_steer({ childId, message }): Appends messages to a run that is still running

Both tools follow the same runtime path ctx.subagents.startActivation({ delivery: 'parent' }); the difference is in the copy. The general tool’s started subagent <id> has no wording knob, and the model has no reason to stop looking for this run; this bundle’s description and results explicitly state that the child agent is already running, no tool can observe it, and results will arrive automatically.

codebuddy_steer wraps the CLI’s ACP steer extension, which is the only channel to get messages while running. The README provides a comparison table:

second session/prompt    →  CLI treats it as a competing round, finishes first round in ~33ms, output is empty
--input-format stream-json →  Starts a new session, the currently running one cannot see it
session/steer            →  {steered: true}, the message is delivered at the next step boundary of the child agent,
                           ongoing tool calls are not interrupted

childId is the id in the activation notice. The provider publishes a live handle in src/steer.ts when session/new returns, and revokes it on run settle, so already finished ids are rejected, and the tool switches to listing live ids instead of guessing. When the child agent returns {steered:false}, it is reported as rejected with a reason and does not retry; the common reason is that the child agent is between rounds.

What the Chat Card Shows

Each delegation generates a card in the transcript (conversation.chat.node, kind is codebuddy-run), anchored to its own tool/call event. The card folds three things: handover result, settle notice, and messages sent mid-run. Since the external child agent only produces these three moments, the range of what the card can honestly express is limited to these:

Status Trigger Source Card Content
Running tool/call Timer starts from call
Running in background tool/result (activation notice) Timer continues; body points to runtime and logs, filling with child agent actions
Completed settle finished Closing statement, attached under task and timeline, CLI’s cost line mentioned in header
Stopped settle was stopped The notice is preserved in full
Rejected settle declined the task Same as above
Failed settle failed before it finished / ended abnormally, or runtime throws error Diagnostic info, plus all - [done] … steps

The principle is “one run, one card.” codebuddy_steer calls in the transcript are also normal tool calls, matching /codebuddy/. If only the name is looked at, it might open a separate card that never settles, stopping at “Running in background” next to the run that just sent a message. So the definition is based on what the call does: arguments containing childId are “speaking to a run” rather than “starting a run,” so they fold into the card of the child they are speaking to (displayed as ↪ Interjection … in the body). If the current page has never seen that card, it folds into an empty one. Its own result is ignored in any case—the run status only changes when the child agent settles. Normal tool rows report the call normally.

The card body is a record, not a replacement: read in order of when the run happens—the child’s task (full prompt, header only puts the caller’s 3-5 word tag, body folds four lines, expandable locally), mid-run messages, things the child did, and finally how it ended. Settle is an append of the closing block, not overwriting existing content. To still hold up after remounting (reconnecting, re-rendering transcript from session log), there is a small cache per run in src/client/live.ts: polling stops after settle; without it, history is lost when re-rendering completed cards.

Two consequences that need to be stated directly:

  • The timer measures waiting, not life. Between the two events, the card hears nothing from the transcript; the browser cannot read the run’s beacon; so the header only counts and explicitly states this, hover text points to codebuddy-runs, where the question can indeed be answered. The actions of the child agent that the card can actually see depend on whether the host responds (see next section).
  • Child agents that die with the host will forever stop at “Running in background,” because there are no events to fold; codebuddy-runs calls this lost connection. The case where the host is alive but the child dies is another scenario: The ACP pipe closes, the run settles with error, and the card turns to “Failed”; this scenario is fixed by crash in tests/mock-acp-child.mjs.

Real-time Progress: The Only Channel Outside the Transcript

Cards that only fold transcript events can only show two moments during a ten-minute run. The child agent’s actions are not in that transcript—they are ACP session/update notifications that arrive at the host process, and the provider already mirrors them into the run logs. src/progress.ts keeps the most recent 60 per run in memory, keyed by the run id used at runtime (i.e., child id), and src/progress-route.ts serves it on the host’s own listener:

GET /subagent-codebuddy/progress?ids=<childId>[,<childId>…]
    → { runs: { <childId>: { steps, updatedAt, endedAt? } } }

The card polls this path during the run (src/client/live.ts, every 1.5s) and stops after settle or three consecutive failures. The README lists four design decisions, one of which is using a relative URL instead of a port, because the web UI is served by the same listener. The action lines the card can see appear as the host responds; “Last action 12s ago” gets older over time; the N steps count in the folded header changes; without this feed, it falls back to pure timing.

This package also comes with a codebuddy-runs command line entry (bin in package.json), for manually viewing run status.

Installation and Enablement

First, let’s clarify one thing: The README I have does not give a one-click install command that can be copied directly, so I won’t assemble one here dsh plugin add .... Looking at package.json, this package declares a bundle structure:

{
  "dsh": {
    "bundle": { "patch": "./cordis.patch.yml" },
    "client": {
      "inject": ["@deepseek-ai/dsh-client-ui-conversation"],
      "platform": "web"
    }
  },
  "engines": { "node": ">=20", "dsh": ">=0.2.1-rc.3" },
  "bin": { "codebuddy-runs": "./bin/codebuddy-runs.mjs" },
  "license": "MIT"
}

In other words: After installing this package, the harness reads dsh.bundle.patch pointing to cordis.patch.yml, where it inserts two lines—the provider line and the tool line mounting this bundle’s ./tool entry. The original text of cordis.patch.yml is as follows:

- insert:
    - id: subagent-codebuddy
      name: 'dsh-subagent-codebuddy'
      config:
        permission: allow
        startupTimeoutMs: 60000
        envFile: ~/.dsh/secrets/codebuddy-api-key
        env:
          CODEBUDDY_INTERNET_ENVIRONMENT: internal
    - id: tool-subagent-codebuddy
      name: 'dsh-subagent-codebuddy/tool'
      config:
        provider: codebuddy
        toolName: subagent_codebuddy
        maxDepth: provider-managed

A few configuration points need to be explained, they all come from comments in the patch’s original text:

  1. permission: allow allows the child agent’s tool prompt to be automatically approved, real tasks can proceed; change to reject when strict read-only children are needed.
  2. envFile: ~/.dsh/secrets/codebuddy-api-key specifies the file holding the API key; the run consumes the subscription quota of the logged-in account, or this envFile provided key.
  3. CODEBUDDY_INTERNET_ENVIRONMENT: internal explicitly specifies the CodeBuddy site used when verifying the key. The domestic site (copilot.tencent.com) and the international site (codebuddy.ai) do not accept each other’s keys, and the CLI selects the site based on the product configuration cached on the machine; machines that have never installed the desktop client will fall back to the international site, causing a completely valid key to report 401. Explicitly writing the name here bypasses this dependency.
  4. maxDepth: provider-managed is a required field and must be explicitly written with this value. If the key is omitted, the harness will fall back to its own default numeric depth, and the CodeBuddy provider does not advertise a depthLimit capability, so it cannot enforce it. This line will fail to activate directly with an error provider "codebuddy" cannot enforce maxDepth .... For external child agents, provider-managed is the only valid value.

Before enabling, you need to satisfy peer dependencies (from peerDependencies): @deepseek-ai/cordis ~4.0.5, and @deepseek-ai/dsh-session, @deepseek-ai/dsh-subagent, @deepseek-ai/dsh-subprocess, @deepseek-ai/dsh-timeout are all ^0.2.1-rc.3; Node requires >=20.

There is also a placement constraint written in the cordis.patch.yml comments: both lines must be placed at the top level of the bundle. If you mount the tool into an agent preset (e.g., preset-cordis), it is tied to that preset, and any profile without that exact preset line cannot mount the tool, so the provider has no path for the model to reach it.

Use Cases and Notes

Who is it for: Developers who are already writing profiles on DSH and want to put subtasks into the local CodeBuddy CLI; people who need external one-shot delegation and can accept the backend feature that “no mid-run progress, results only come back once at settle.” If you need the child agent to send and receive messages mid-run bidirectionally, this plugin does not satisfy it; it only provides one-way steer from parent to child.

Points to note:

  • The plugin runs with the permissions of the current DSH process, will read the key file pointed to by envFile, and will spawn the local codebuddy --acp subprocess. Read the source code and LICENSE (MIT) before installing to confirm you can accept this permission surface.
  • Expenses go through the CodeBuddy account’s subscription quota or the key provided by envFile, not the dsh side’s quota.
  • There is no mid-run progress channel, do not expect the model to see anything during the waiting period on the parameters. The tool has already tried its best with the copy to prevent the parent agent from waiting in vain, but before settle, the parent session cannot get the child agent’s actions unless the host provides the progress feed from the previous section.
  • When the child agent dies with the host, it will permanently stop at “Running in background,” because there are no events to fold; this is a design boundary, not a fixable bug.
  • Stars are 0, the repo was created in the last few days, and in the ecosystem it is a very new small project. It is worth checking the repo’s activity and dependency versions yourself before adopting.
  • The community directory site is an independent site, the plugin is not an official release by DeepSeek / Huansuan.

Conclusion

The value of this package is not in doing something others can’t, but in clarifying a boundary case: External one-shot child agents have no mid-run progress; the four layers of tool copy, steer channel, chat card, and progress feed are all designed around the goal of “preventing the parent agent from waiting in vain and letting results arrive honestly.” If your profile needs to delegate to the local CodeBuddy CLI, it is an implementation that can be directly put into the bundle.

  • Directory page: https://www.skillhub.cn/plugins/lloydzhou/dsh-subagent-codebuddy
  • GitHub: https://github.com/lloydzhou/dsh-subagent-codebuddy
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye