Introduction¶
When using multi-agent orchestration in DSH, a common pitfall is: an upstream disconnection kills the child agent, but the parent agent thinks it’s still running normally.
The reason is that AgentStatus only has 'idle' | 'running', without failed:
- The semantics of
idleinagent/statusis “no driver remains scheduled or active”; list_agentsonly displaysrunning/inactive;- The parent’s
subagent-settlednotice is not sent on the disconnection path.
Thus, “finished normally”, “waiting for me to respond”, and “died due to upstream disconnection” look exactly the same to the parent agent. Currently, the only reliable signal is polling the task board to see if the task is still in_progress but members are inactive, which is unacceptable in multi-agent orchestration. Lequait/dsh-subagent-watchdog is the plugin to fill this gap: when a child agent dies due to upstream disconnection, it reports the death to the parent agent on its behalf.
Below is an introduction to its positioning, installation methods, and configuration options.
What is it¶
One-sentence positioning: Listen to agent/error, and after confirming the child agent has not recovered, deliver a handleable interrupt notification to the parent agent. It does not retry anything and does not modify session history; it is only responsible for letting the parent agent know “this child agent is dead, you decide”.
- Maintainer: Lequait
- Category: Workflow
- Stars: 0
- License: MIT
- Version: 0.1.0 (from
package.json) - Dependencies:
schemastery, Node>=20,dsh>=0.2.0-rc.1
Core Features¶
It does four things:
- Listen to
agent/errorin the root scope. Root listeners can receive all agent events. - Resolve the parent agent of the child agent: first check ownership via
ctx.agents.isOwnedBy(), then fall back to the fork lineagesession.header.parentSession. - Wait for
settleDelayMs(default 2500ms):- Child agent has returned to
running→ harness itself retried successfully, do not send; - Child agent is still
idle→ it hasn’t recovered, send.
- Child agent has returned to
- Deliver the notification using
parent.followup(message): The parent agent is woken up when idle, or queued after the current turn if it is running.
The notification received by the parent agent looks roughly like this:
[subagent-watchdog] Child agent interrupted
Child agent: 01J8X...
Level: 1
Turn/Step: turn 2 / step 5
Error: socket hang up code=ECONNRESET
Status: Stopped (idle), will not recover on its own
Please decide how to handle:
- Redeploy the same task (spawn a new child agent)
- Retry with a different provider/model
- Take over yourself
- Give up and log it
The end of the message also includes a note: “If you have already received a notification of normal completion for this child agent, please ignore this message.”
Installation and Activation¶
The README provides three methods; choose one based on your scenario.
Method 1: Absolute Path (Local development, no npm needed)¶
Append to ~/.dsh/cordis.patch.yml:
- insert:
- id: subagent-watchdog
name: "H:/AI/dsh-subagent-watchdog/index.js"
The loader converts the absolute path to a file:// URL, so you don’t need the profile’s node_modules link. Note that it must be a - insert: list—the patch at the profile layer is id-targeted, so a bare - id: will be treated as “patch already exists”. You need to run npm install once in the plugin directory for the dependency schemastery.
Method 2: Install from Repository¶
dsh plugin --profile <name> add git+https://github.com/Lequait/dsh-subagent-watchdog.git
Method 3: Install from Plugin Market¶
Add the index address below to the Discover index source, then install it from the market:
https://github.com/Lequait/dsh-subagent-watchdog/raw/main/market/index.json
jsDelivr mirror (faster in China):
https://cdn.jsdelivr.net/gh/Lequait/dsh-subagent-watchdog@main/market/index.json
Configuration¶
All are optional, written under config: in the patch entry:
- insert:
- id: subagent-watchdog
name: "H:/AI/dsh-subagent-watchdog/index.js"
config:
settleDelayMs: 2500
cooldownMs: 60000
dryRun: false
| Option | Default | Description |
|---|---|---|
enabled |
true |
Master switch |
settleDelayMs |
2500 |
How long to wait after agent/error before determining death. If it recovers to running during this period, do not send |
cooldownMs |
60000 |
Minimum interval between two notifications for the same child agent |
maxPerChild |
5 |
Maximum notifications for the same child agent (per process) |
notifyOnDisposed |
true |
Report even when the child agent is disposed with an unhandled failure |
dryRun |
false |
Log only, do not deliver; recommended to enable during first installation |
messagePrefix |
[subagent-watchdog] |
Notification prefix |
Applicable Scenarios and Notes¶
Suitable for those using DSH for multi-agent orchestration and splitting tasks to child agents. It runs with the permissions of the current dsh process, allowing it to listen to all agent events in the root scope and send messages to the parent agent; you should check the source code and license (MIT) before installing.
It also has some limitations, which are explicitly listed in the README:
- Only covers one way of dying: “stopped after an error”. When the upstream hangs silently (stream never returns and doesn’t report an error),
agent/errorwill not trigger, so this plugin remains silent. In this case, rely on the provider’sstreamIdleTimeoutMs(default 5 minutes) to convert it to aTIMEOUT. - Possible duplicate notifications. If the harness one day adds back the
subagent-settlednotice for the disconnection path, the parent agent will receive both the official notice and the plugin’s notice. The message body already states “If you have already received a notification of normal completion, please ignore”. - Does not distinguish error types. Tool errors, provider disconnections, and context overflows will all trigger
agent/error. The judgment criterion is “no recovery after an error”, not the error code. - Depends on the
ctx.agentsservice;inject = ['agents']ensures it is mounted only after the service is ready.
Conclusion¶
This plugin solves a very specific problem: it doesn’t add retries for the child agent, but reliably tells the parent agent “this child agent is dead,” handing the decision-making power back. For those orchestrating based on the idle / running binary states, it turns a scenario that previously could only be judged by polling the task board into a handleable message delivered to the parent agent.
Directory page: https://www.skillhub.cn/plugins/Lequait/dsh-subagent-watchdog
GitHub: https://github.com/Lequait/dsh-subagent-watchdog