liangmianya/dsh-synapse
liangmianya/dsh-synapseDeepSeek Harness 的可视化非线性对话工作区:把会话、追问与分支变成可浏览、可拖拽的对话地图。
带归属校验的工具与 HTTP 路由,用于列出、释放和永久删除子代理会话,删除后自动刷新 web UI。
DeepSeek Harness (DSH) plugin that adds the missing subagent lifecycle interface:
delete_subagent — permanently delete a subagent session and drop it from the session listrelease_subagent — stop / release a subagent without deleting its transcriptlist_subagents — list the calling session's descendant subagents (including finished one-shot subagents)Verified against DSH 0.2.1-alpha.2; compatibility shims keep 0.2.0-rc.2 and
>=0.1.0-rc.8 <0.2.0-0 working (declared range:
>=0.1.0-rc.8 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0).
DSH's built-in subagent control surface only provides send_message, interrupt_agent
and list_agents. There is no delete/release endpoint, so finished one-shot and stale
continuable subagents keep accumulating in the web UI list. This plugin fills that gap
with an ownership-checked, model-callable tool plus optional HTTP routes.
dsh plugin --profile web add github:heiheiha798/dsh-plugin-subagent-delete
# or pin to the published release
dsh plugin --profile web add github:heiheiha798/dsh-plugin-subagent-delete#v0.2.3
# or from a local checkout
dsh plugin --profile web add /path/to/dsh-plugin-subagent-delete
Restart the profile, then start a new session. The session gains three tools.
list_subagentslist_subagents({}) // all descendants
list_subagents({ activity: "running" }) // running only
list_subagents({ mode: "one-shot" }) // one-shot only
Returns the caller's descendant subagent tree: id, label, mode
(one-shot | continuable | external | unknown), activity
(running | inactive), depth, parentId, hasChildren and present
(whether the durable session artifact is still stored). A child whose catalog
branch could not be read carries unreadable
(corrupt | unsupported | unavailable) and still counts as a deletable
target. Children whose durable session was already permanently deleted are
omitted; pass include_absent: true to list them (they report
present: false).
delete_subagentdelete_subagent({ subagent_id: "0fcfbdd6-5d21-46a4-bd95-2ca6edac1261" })
delete_subagent({ subagent_id: "<id>", recursive: true })
Permanently removes the subagent:
ctx.subagents.listDescendants,
falling back to a persisted-header lineage walk);ctx.agentTeams.dismissTeammate, so
the Team panel stops listing the teammate;ctx.subagents.drainChildren(parent, childIds) seam, which also
detaches the child Session from the live store;ctx.sessions;session_projcache row;Without recursive: true a target that still has descendants is refused with
has-descendants. Deletion is child-first and permanent. A target whose durable
session is already gone reports already_absent instead of not-found, so a
subtree delete never aborts half-way. Each deleted row reports dismissed,
which stays false when the target is not a roster member, the caller is not the
Lead, or the composition mounts no Team service.
release_subagentrelease_subagent({ subagent_id: "<id>" })
release_subagent({ subagent_id: "<id>", recursive: true })
Stops the current turn and releases a resident child Activation via
ctx.subagents.drainChildren(parent, childIds), but keeps the durable
transcript. The subagent remains resumable with send_message.
A permanent delete removes the durable child, but an open client keeps its
cached session list and projections until something invalidates them. This
plugin works around that gap without patching DSH: after every permanent delete
the host publishes a short-lived marker session through the official
prepare → enter → announce → detach lifecycle seam. The bundled web client
component (lib/client.js) watches the public sessions.list snapshot and,
when the marker child is removed (api-session/removed), debounces 350 ms and
calls sessions.refresh() plus sessions.refreshProjections(parentId) for the
affected parents — the current dsh face (pre-0.2.1 releases used
sessions.refreshSubagents(parentId) and are still supported). Open web clients
therefore drop the deleted subagent without a manual page reload. The marker's
_no-cwd artifact is swept after detach, so the refresh glue leaves no residue.
Team sessions additionally lose the teammate from the Team panel: a teammate is
a durable team/member fact, and delete_subagent terminates it through the
official Lead-only dismissal before removing the Session.
One honest limit remains: the parent Session's subagent/catalog facts are
append-only durable history, so a deleted child can still appear as a
historical row in the parent's header catalog dropdown until that parent log
itself moves on. Removing the fact would mean rewriting a committed session
log, which dsh does not support. The sidebar session list is refreshed
normally.
For client integration and local debugging the host plugin registers:
GET /dsh-plugin-subagent-delete/list?parentSessionId=<id>[&activity=…][&mode=…]POST /dsh-plugin-subagent-delete/delete { "parentSessionId": "<id>", "subagentId": "<id>", "recursive": false }POST /dsh-plugin-subagent-delete/release { "parentSessionId": "<id>", "subagentId": "<id>", "recursive": false }HTTP callers must name the same parentSessionId the target descends from; the
same ownership check runs on every mutation.
not-your-subagent; an id whose durable session is already
gone returns already_absent rather than a hard failure.npm test # node --test
npm run check # syntax check (host + client bundle) + tests
npm run verify:dsh # contract probe against published dsh 0.2.1-alpha.2
npm run verify:dsh 0.2.2-rc.1 # ...against another published release
npm run pack:dry
The test suite includes unit tests and an integration fixture created from ten
real one-shot test subagents (see test/fixtures/test-subagents.json).
npm run verify:dsh installs the requested published @deepseek-ai/dsh-*
release into a temp directory and drives src/index.js through the real
ToolRuntime from those artifacts (registration, schema compilation, and
execution of all three tools) — use it to re-check the plugin against a new dsh
release without building the harness checkout.
MIT